From 5d1566107e32d0ad552af995960d9beff2e9ad2b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 12 Oct 2015 00:39:48 -0400
Subject: [PATCH 001/752] Initial commit

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..92159fdbc5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# synchronet-web-v4
+A web interface for Synchronet BBS
-- 
GitLab


From 195171a1bdd02c6be785cfd1c4bdda8f365c72ba Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 12 Oct 2015 01:32:07 -0400
Subject: [PATCH 002/752] Initial commit.

---
 mods/websocket-proxy.js                       |  432 ++
 mods/websocket-rlogin-service.js              |   92 +
 text/synch.ans                                |   21 +
 web/lib/auth.js                               |  184 +
 web/lib/files.js                              |   38 +
 web/lib/forum.js                              |  571 ++
 web/lib/ftelnet.js                            |   14 +
 web/lib/init.js                               |   52 +
 web/lib/mime-decode.js                        |  283 +
 web/lib/pages.js                              |  151 +
 web/lib/sidebar.js                            |   75 +
 web/root/api/attachments.ssjs                 |   58 +
 web/root/api/auth.ssjs                        |   11 +
 web/root/api/files.ssjs                       |   65 +
 web/root/api/forum.ssjs                       |  146 +
 web/root/api/register.ssjs                    |  180 +
 web/root/api/system.ssjs                      |  101 +
 web/root/bootstrap/css/bootstrap-theme.css    |  587 ++
 .../bootstrap/css/bootstrap-theme.css.map     |    1 +
 .../bootstrap/css/bootstrap-theme.min.css     |    5 +
 web/root/bootstrap/css/bootstrap.css          | 6800 +++++++++++++++++
 web/root/bootstrap/css/bootstrap.css.map      |    1 +
 web/root/bootstrap/css/bootstrap.min.css      |    5 +
 .../fonts/glyphicons-halflings-regular.eot    |  Bin 0 -> 20127 bytes
 .../fonts/glyphicons-halflings-regular.svg    |  288 +
 .../fonts/glyphicons-halflings-regular.ttf    |  Bin 0 -> 45404 bytes
 .../fonts/glyphicons-halflings-regular.woff   |  Bin 0 -> 23424 bytes
 .../fonts/glyphicons-halflings-regular.woff2  |  Bin 0 -> 18028 bytes
 web/root/bootstrap/js/bootstrap.js            | 2363 ++++++
 web/root/bootstrap/js/bootstrap.min.js        |    7 +
 web/root/bootstrap/js/npm.js                  |   13 +
 web/root/css/navbar-fixed-top.css             |    9 +
 web/root/css/offcanvas.css                    |   59 +
 web/root/css/style.css                        |   64 +
 web/root/error/400.html                       |    9 +
 web/root/error/401.html                       |    9 +
 web/root/error/403.html                       |    9 +
 web/root/error/404.html                       |    8 +
 web/root/error/414.html                       |    9 +
 web/root/error/416.html                       |    9 +
 web/root/error/500.html                       |    9 +
 web/root/error/501.html                       |    9 +
 web/root/images/favicon.ico                   |  Bin 0 -> 3774 bytes
 web/root/index.xjs                            |  200 +
 web/root/js/common.js                         |  118 +
 web/root/js/forum.js                          |  226 +
 web/root/js/jquery.min.js                     |    6 +
 web/root/js/offcanvas.js                      |    5 +
 web/root/js/validator.js                      |  325 +
 web/root/pages/000-home.xjs                   |   24 +
 web/root/pages/000-mail.ssjs                  |   89 +
 web/root/pages/000-register.xjs               |  192 +
 web/root/pages/001-forum.ssjs                 |  379 +
 web/root/pages/002-files.ssjs                 |  118 +
 web/root/pages/003-games.xjs                  |   58 +
 web/root/pages/003_userlist.xjs               |  190 +
 web/root/pages/webctrl.ini                    |    8 +
 web/root/sidebar/001-nodelist.xjs             |   42 +
 web/root/sidebar/003-systemStats.xjs          |   57 +
 59 files changed, 14784 insertions(+)
 create mode 100644 mods/websocket-proxy.js
 create mode 100644 mods/websocket-rlogin-service.js
 create mode 100644 text/synch.ans
 create mode 100644 web/lib/auth.js
 create mode 100644 web/lib/files.js
 create mode 100644 web/lib/forum.js
 create mode 100644 web/lib/ftelnet.js
 create mode 100644 web/lib/init.js
 create mode 100644 web/lib/mime-decode.js
 create mode 100644 web/lib/pages.js
 create mode 100644 web/lib/sidebar.js
 create mode 100644 web/root/api/attachments.ssjs
 create mode 100644 web/root/api/auth.ssjs
 create mode 100644 web/root/api/files.ssjs
 create mode 100644 web/root/api/forum.ssjs
 create mode 100644 web/root/api/register.ssjs
 create mode 100644 web/root/api/system.ssjs
 create mode 100644 web/root/bootstrap/css/bootstrap-theme.css
 create mode 100644 web/root/bootstrap/css/bootstrap-theme.css.map
 create mode 100644 web/root/bootstrap/css/bootstrap-theme.min.css
 create mode 100644 web/root/bootstrap/css/bootstrap.css
 create mode 100644 web/root/bootstrap/css/bootstrap.css.map
 create mode 100644 web/root/bootstrap/css/bootstrap.min.css
 create mode 100644 web/root/bootstrap/fonts/glyphicons-halflings-regular.eot
 create mode 100644 web/root/bootstrap/fonts/glyphicons-halflings-regular.svg
 create mode 100644 web/root/bootstrap/fonts/glyphicons-halflings-regular.ttf
 create mode 100644 web/root/bootstrap/fonts/glyphicons-halflings-regular.woff
 create mode 100644 web/root/bootstrap/fonts/glyphicons-halflings-regular.woff2
 create mode 100644 web/root/bootstrap/js/bootstrap.js
 create mode 100644 web/root/bootstrap/js/bootstrap.min.js
 create mode 100644 web/root/bootstrap/js/npm.js
 create mode 100644 web/root/css/navbar-fixed-top.css
 create mode 100644 web/root/css/offcanvas.css
 create mode 100644 web/root/css/style.css
 create mode 100644 web/root/error/400.html
 create mode 100644 web/root/error/401.html
 create mode 100644 web/root/error/403.html
 create mode 100644 web/root/error/404.html
 create mode 100644 web/root/error/414.html
 create mode 100644 web/root/error/416.html
 create mode 100644 web/root/error/500.html
 create mode 100644 web/root/error/501.html
 create mode 100644 web/root/images/favicon.ico
 create mode 100644 web/root/index.xjs
 create mode 100644 web/root/js/common.js
 create mode 100644 web/root/js/forum.js
 create mode 100644 web/root/js/jquery.min.js
 create mode 100644 web/root/js/offcanvas.js
 create mode 100644 web/root/js/validator.js
 create mode 100644 web/root/pages/000-home.xjs
 create mode 100644 web/root/pages/000-mail.ssjs
 create mode 100644 web/root/pages/000-register.xjs
 create mode 100644 web/root/pages/001-forum.ssjs
 create mode 100644 web/root/pages/002-files.ssjs
 create mode 100644 web/root/pages/003-games.xjs
 create mode 100644 web/root/pages/003_userlist.xjs
 create mode 100644 web/root/pages/webctrl.ini
 create mode 100644 web/root/sidebar/001-nodelist.xjs
 create mode 100644 web/root/sidebar/003-systemStats.xjs

diff --git a/mods/websocket-proxy.js b/mods/websocket-proxy.js
new file mode 100644
index 0000000000..d68d017a5f
--- /dev/null
+++ b/mods/websocket-proxy.js
@@ -0,0 +1,432 @@
+load("sha1.js");
+
+var WebSocketProxy = function(client) {
+
+	var WEBSOCKET_NEED_PACKET_START = 0;
+	var WEBSOCKET_NEED_PAYLOAD_LENGTH = 1;
+	var WEBSOCKET_NEED_MASKING_KEY = 2;
+	var WEBSOCKET_DATA = 3;
+
+	var FFrameMask = [];
+	var FFrameOpCode = 0;
+	var FFramePayloadLength = 0;
+	var FFramePayloadReceived = 0;
+	var FWebSocketState = WEBSOCKET_NEED_PACKET_START;
+
+	var self = this;
+	this.headers = [];
+
+	var ClientDataBuffer = []; // From client
+	var ServerDataBuffer = []; // From server
+
+	function CalculateWebSocketKey(InLine) {
+		var Digits = "";
+		var Spaces = 0;
+		for (var i = 0; i < InLine.length; i++) {
+			if (InLine.charAt(i) == " ") {
+				Spaces++;
+			} else if (!isNaN(InLine.charAt(i))) {
+				Digits += InLine.charAt(i);
+			}
+		}
+		return Digits / Spaces;
+	}
+
+	function GetFromWebSocketClient() {
+		switch (self.headers['Version']) {
+			case 0: return GetFromWebSocketClientDraft0();
+			case 7: 
+			case 8: 
+			case 13:
+				return GetFromWebSocketClientVersion7();
+		}
+	}
+
+	function GetFromWebSocketClientDraft0() {
+		var Result = [];
+		var InByte = 0;
+		var InByte2 = 0;
+		var InByte3 = 0;
+		
+		while (client.socket.data_waiting) {
+			InByte = client.socket.recvBin(1);
+			
+			// Check what the client packet state is
+			switch (FWebSocketState) {
+				case WEBSOCKET_NEED_PACKET_START:
+					// Check for 0x00 to indicate the start of a data packet
+					if (InByte === 0x00) {
+						FWebSocketState = WEBSOCKET_DATA;
+					}
+					break;
+				case WEBSOCKET_DATA:
+					// We're in a data packet, so check for 0xFF, which indicates the data packet is done
+					if (InByte == 0xFF) {
+						FWebSocketState = WEBSOCKET_NEED_PACKET_START;
+					} else {
+						// Check if the byte needs to be UTF-8 decoded
+						if (InByte < 128) {
+							Result.push(InByte);
+						} else if ((InByte > 191) && (InByte < 224)) {
+							// Handle UTF-8 decode
+							InByte2 = client.socket.recvBin(1);
+							Result.push(((InByte & 31) << 6) | (InByte2 & 63));
+						} else {
+							// Handle UTF-8 decode (should never need this, but included anyway)
+							InByte2 = client.socket.recvBin(1);
+							InByte3 = client.socket.recvBin(1);
+							Result.push(((InByte & 15) << 12) | ((InByte2 & 63) << 6) | (InByte3 & 63));
+						}
+					}
+					break;
+			}
+		}
+		
+		return Result;
+	}
+
+	function GetFromWebSocketClientVersion7() {
+		var Result = [];
+		var InByte = 0;
+		var InByte2 = 0;
+		var InByte3 = 0;
+
+		while (client.socket.data_waiting) {
+			// Check what the client packet state is
+			switch (FWebSocketState) {
+				case WEBSOCKET_NEED_PACKET_START:
+					// Next byte will give us the opcode, and also tell is if the message is fragmented
+					FFrameMask = [];
+					FFrameOpCode = client.socket.recvBin(1);
+					FFramePayloadLength = 0;
+					FFramePayloadReceived = 0;
+					FWebSocketState = WEBSOCKET_NEED_PAYLOAD_LENGTH;
+					break;
+				case WEBSOCKET_NEED_PAYLOAD_LENGTH:
+					FFramePayloadLength = (client.socket.recvBin(1) & 0x7F);
+					if (FFramePayloadLength === 126) {
+						FFramePayloadLength = client.socket.recvBin(2);
+					} else if (FFramePayloadLength === 127) {
+						FFramePayloadLength = client.socket.recvBin(8);
+					}
+					FWebSocketState = WEBSOCKET_NEED_MASKING_KEY;
+					break;
+				case WEBSOCKET_NEED_MASKING_KEY:
+					InByte = client.socket.recvBin(4);
+					FFrameMask[0] = (InByte & 0xFF000000) >> 24;
+					FFrameMask[1] = (InByte & 0x00FF0000) >> 16;
+					FFrameMask[2] = (InByte & 0x0000FF00) >> 8;
+					FFrameMask[3] = InByte & 0x000000FF;
+					FWebSocketState = WEBSOCKET_DATA;
+					break;
+				case WEBSOCKET_DATA:
+					InByte = (client.socket.recvBin(1) ^ FFrameMask[FFramePayloadReceived++ % 4]);
+
+					// Check if the byte needs to be UTF-8 decoded
+					if ((InByte & 0x80) === 0) {
+						Result.push(InByte);
+					} else if ((InByte & 0xE0) === 0xC0) {
+						// Handle UTF-8 decode
+						InByte2 = (client.socket.recvBin(1) ^ FFrameMask[FFramePayloadReceived++ % 4]);
+						Result.push(((InByte & 31) << 6) | (InByte2 & 63));
+					} else {
+						log(LOG_ERR, "Byte out of range: " + InByte);
+					}
+
+					// Check if we've received the full payload
+					if (FFramePayloadReceived === FFramePayloadLength) FWebSocketState = WEBSOCKET_NEED_PACKET_START;
+					break;
+			}
+		}
+
+		return Result;
+	}
+
+	function SendToWebSocketClient(AData) {
+		switch (self.headers['Version']) {
+			case 0: 
+				SendToWebSocketClientDraft0(AData); 
+				break;
+			case 7: 
+			case 8: 
+			case 13:
+				SendToWebSocketClientVersion7(AData); 
+				break;
+		}
+	}
+
+	function SendToWebSocketClientDraft0(AData) {
+		// Send 0x00 to indicate the start of a data packet
+		client.socket.sendBin(0x00, 1);
+
+		for (var i = 0; i < AData.length; i++) {
+			// Check if the byte needs to be UTF-8 encoded
+			if ((AData[i] & 0xFF) <= 127) {
+				client.socket.sendBin(AData[i], 1);
+			} else if ((AData[i] & 0xFF) <= 2047) {
+				// Handle UTF-8 encode
+				client.socket.sendBin((AData[i] >> 6) | 192, 1);
+				client.socket.sendBin((AData[i] & 63) | 128, 1);
+			} else {
+				log(LOG_ERR, "Byte out of range: " + AData[i]);
+			}
+		}
+
+		// Send 0xFF to indicate the end of a data packet
+		client.socket.sendBin(0xFF, 1);
+	}
+
+	function SendToWebSocketClientVersion7(AData) {
+		if (AData.length > 0) {
+			var ToSend = [];
+
+			for (var i = 0; i < AData.length; i++) {
+				// Check if the byte needs to be UTF-8 encoded
+				if ((AData[i] & 0xFF) <= 127) {
+					ToSend.push(AData[i]);
+				} else if (((AData[i] & 0xFF) >= 128) && ((AData[i] & 0xFF) <= 2047)) {
+					// Handle UTF-8 encode
+					ToSend.push((AData[i] >> 6) | 192);
+					ToSend.push((AData[i] & 63) | 128);
+				} else {
+					log(LOG_ERR, "Byte out of range: " + AData[i]);
+				}
+			}
+
+			client.socket.sendBin(0x81, 1);
+			if (ToSend.length <= 125) {
+				client.socket.sendBin(ToSend.length, 1);
+			} else if (ToSend.length <= 65535) {
+				client.socket.sendBin(126, 1);
+				client.socket.sendBin(ToSend.length, 2);
+			} else {
+				// NOTE: client.socket.sendBin(ToSend.length, 8); didn't work, so this
+				//       modification limits the send to 2^32 bytes (probably not an issue)
+				//       Probably should look into a proper fix at some point though
+				client.socket.sendBin(127, 1);
+				client.socket.sendBin(0, 4);
+				client.socket.sendBin(ToSend.length, 4);
+			}
+			
+			for (var i = 0; i < ToSend.length; i++) {
+				client.socket.sendBin(ToSend[i] & 0xFF, 1);
+			}
+		}
+	}
+
+	function ShakeHands() {
+		self.headers['Version'] = 0;
+
+		try {
+			// Keep reading header data until we get all the data we want
+			while (true) {
+				// Read another line, and abort if we don't get one within 5 seconds
+				var InLine = client.socket.recvline(1024, 5);
+				if (InLine === null) {
+					log(LOG_ERR, "Timeout exceeded while waiting for complete handshake");
+					return false;
+				}
+
+				log(LOG_DEBUG, "Handshake Line: " + InLine);
+				
+				// Check for blank line (indicates we have most of the header, and only the last 8 bytes remain
+				if (InLine === "") {
+					switch (self.headers['Version']) {
+						case 0: 
+							return ShakeHandsDraft0();
+						case 7: 
+						case 8: 
+						case 13:
+							return ShakeHandsVersion7();
+						default: 
+							//		    TODO If this version does not
+							//          match a version understood by the server, the server MUST
+							//          abort the websocket handshake described in this section and
+							//          instead send an appropriate HTTP error code (such as 426
+							//          Upgrade Required), and a |Sec-WebSocket-Version| header
+							//          indicating the version(s) the server is capable of
+							//          understanding.
+							break;
+					}
+					break;
+				} else if (InLine.indexOf("Connection:") === 0) {
+					// Example: "Connection: Upgrade"
+					self.headers['Connection'] = InLine.replace(/Connection:\s?/i, "");
+				} else if (InLine.indexOf("GET") === 0) {
+					// Example: "GET /demo HTTP/1.1"
+					var GET = InLine.split(" ");
+					self.headers['Path'] = GET[1];
+				} else if (InLine.indexOf("Host:") === 0) {
+					// Example: "Host: example.com"
+					self.headers['Host'] = InLine.replace(/Host:\s?/i, "");
+				} else if (InLine.indexOf("Origin:") === 0) {
+					// Example: "Origin: http://example.com"
+					self.headers['Origin'] = InLine.replace(/Origin:\s?/i, "");
+				} else if (InLine.indexOf("Sec-WebSocket-Key:") === 0) {
+					// Example: "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=="
+					self.headers['Key'] = InLine.replace(/Sec-WebSocket-Key:\s?/i, "");
+				} else if (InLine.indexOf("Sec-WebSocket-Key1:") === 0) {
+					// Example: "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5"
+					self.headers['Key1'] = CalculateWebSocketKey(InLine.replace(/Sec-WebSocket-Key1:\s?/i, ""));
+				} else if (InLine.indexOf("Sec-WebSocket-Key2:") === 0) {
+					// Example: "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00"
+					self.headers['Key2'] = CalculateWebSocketKey(InLine.replace(/Sec-WebSocket-Key2:\s?/i, ""));
+				} else if (InLine.indexOf("Sec-WebSocket-Origin:") === 0) {
+					// Example: "Sec-WebSocket-Origin: http://example.com"
+					self.headers['Origin'] = InLine.replace(/Sec-WebSocket-Origin:\s?/i, "");
+				} else if (InLine.indexOf("Sec-WebSocket-Protocol:") === 0) {
+					// Example: "Sec-WebSocket-Protocol: sample"
+					self.headers['SubProtocol'] = InLine.replace(/Sec-WebSocket-Protocol:\s?/i, "");
+				} else if (InLine.indexOf("Sec-WebSocket-Draft") === 0) {
+					// Example: "Sec-WebSocket-Draft: 2"
+					try {
+						self.headers['Version'] = parseInt(InLine.replace(/Sec-WebSocket-Draft:\s?/i, ""));
+					} catch (err) {
+						self.headers['Version'] = 0;
+					}
+				} else if (InLine.indexOf("Sec-WebSocket-Version") === 0) {
+					// Example: "Sec-WebSocket-Version: 8"
+					try {
+						self.headers['Version'] = parseInt(InLine.replace(/Sec-WebSocket-Version:\s?/i, ""));
+					} catch (err) {
+						self.headers['Version'] = 0;
+					}
+				} else if (InLine.indexOf("Upgrade:") === 0) {
+					// Example: "Upgrade: websocket"
+					self.headers['Upgrade'] = InLine.replace(/Upgrade:\s?/i, "");
+				} else if (InLine.indexOf("Cookie:") === 0) {
+				 	self.headers['Cookie'] = InLine.replace(/Cookie:\s?/i, "");
+				}
+			}
+		} catch (err) {
+			log(LOG_ERR, "ShakeHands() error: " + err.toString());
+		}
+		
+		return false;
+	}
+
+	function ShakeHandsDraft0() {
+		// Ensure we have all the data we need
+		if (('Key1' in self.headers) && ('Key2' in self.headers) && ('Host' in self.headers) && ('Origin' in self.headers !== "") && ('Path' in self.headers)) {
+			// Combine Key1, Key2, and the last 8 bytes into a string that we will later hash
+			var ToHash = ""
+			ToHash += String.fromCharCode((self.headers['Key1'] & 0xFF000000) >> 24);
+			ToHash += String.fromCharCode((self.headers['Key1'] & 0x00FF0000) >> 16);
+			ToHash += String.fromCharCode((self.headers['Key1'] & 0x0000FF00) >> 8);
+			ToHash += String.fromCharCode((self.headers['Key1'] & 0x000000FF) >> 0);
+			ToHash += String.fromCharCode((self.headers['Key2'] & 0xFF000000) >> 24);
+			ToHash += String.fromCharCode((self.headers['Key2'] & 0x00FF0000) >> 16);
+			ToHash += String.fromCharCode((self.headers['Key2'] & 0x0000FF00) >> 8);
+			ToHash += String.fromCharCode((self.headers['Key2'] & 0x000000FF) >> 0);				
+			for (var i = 0; i < 8; i++) {
+				ToHash += String.fromCharCode(client.socket.recvBin(1));
+			}
+			
+			// Hash the string
+			var Hashed = md5_calc(ToHash, true);
+
+			// Setup the handshake response
+			var Response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
+						   "Upgrade: WebSocket\r\n" +
+						   "Connection: Upgrade\r\n" +
+						   "Sec-WebSocket-Origin: " + self.headers['Origin'] + "\r\n" +
+						   "Sec-WebSocket-Location: ws://" + self.headers['Host'] + self.headers['Path'] + "\r\n";
+			if ('SubProtocol' in self.headers) Response += "Sec-WebSocket-Protocol: " + self.headers['SubProtocol'] + "\r\n";
+			Response += "\r\n";
+			
+			// Loop through the hash string (which is hex encoded) and append the individual bytes to the response
+			for (var i = 0; i < Hashed.length; i += 2) {
+				Response += String.fromCharCode(parseInt(Hashed.charAt(i) + Hashed.charAt(i + 1), 16));
+			}
+			
+			// Send the response and return
+			client.socket.send(Response);
+			return true;
+		} else {
+			// We're missing some pice of data, log what we do have
+			log(LOG_ERR, "Missing some piece of handshake data.  Here's what we have:");
+			for(var x in self.headers) { 
+				log(LOG_ERR, x + " => " + self.headers[x]); 
+			}
+			return false;
+		}
+	}
+
+	function ShakeHandsVersion7() {
+		// Ensure we have all the data we need
+		if (('Key' in self.headers) && ('Host' in self.headers) && ('Origin' in self.headers !== "") && ('Path' in self.headers)) {
+			var AcceptGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+			// Combine Key and GUID
+			var ToHash = self.headers['Key'] + AcceptGUID;
+			
+			// Hash the string
+			var Hashed = Sha1.hash(ToHash, false);
+
+			// Encode the hash
+			var ToEncode = '';
+			for (var i = 0; i <= 38; i += 2) {
+				ToEncode += String.fromCharCode(parseInt(Hashed.substr(i, 2), 16));
+			}
+			var Encoded = base64_encode(ToEncode);
+
+			// Setup the handshake response
+			var Response = "HTTP/1.1 101 Switching Protocols\r\n" +
+						   "Upgrade: websocket\r\n" +
+						   "Connection: Upgrade\r\n" +
+						   "Sec-WebSocket-Accept: " + Encoded + "\r\n";
+			if ('SubProtocol' in self.headers) Response += "Sec-WebSocket-Protocol: plain\r\n"; // Only sub-protocol we support
+			Response += "\r\n";
+			
+			// Send the response and return
+			client.socket.send(Response);
+			return true;
+		} else {
+			// We're missing some pice of data, log what we do have
+			log(LOG_ERR, "Missing some piece of handshake data.  Here's what we have:");
+			for(var x in self.headers) { 
+				log(LOG_ERR, x + " => " + self.headers[x]); 
+			}
+			return false;
+		}
+	}
+
+	this.__defineGetter__(
+		"data_waiting",
+		function() {
+			return (ClientDataBuffer.length > 0);
+		}
+	);
+
+	this.send = function(data) {
+		if(typeof data == "string") {
+			data = data.split("").map(
+				function(d) {
+					return ascii(d);
+				}
+			);
+		}
+		ServerDataBuffer = ServerDataBuffer.concat(data);
+	}
+
+	this.receive = function() {
+		var data = "";
+		while(ClientDataBuffer.length > 0) {
+			data += ascii(ClientDataBuffer.shift());
+		}
+		return data;
+	}
+
+	this.receiveArray = function(len) {
+		return ClientDataBuffer.splice(0, (typeof len == "number" ? len : ClientDataBuffer.length));
+	}
+
+	this.cycle = function() {
+		ClientDataBuffer = ClientDataBuffer.concat(GetFromWebSocketClient());
+		SendToWebSocketClient(ServerDataBuffer.splice(0, 4096));
+	}
+
+	if(!ShakeHands())
+		throw "ShakeHands() failed";
+
+}
\ No newline at end of file
diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
new file mode 100644
index 0000000000..109b826884
--- /dev/null
+++ b/mods/websocket-rlogin-service.js
@@ -0,0 +1,92 @@
+load("sbbsdefs.js");
+load("websocket-proxy.js");
+
+var err = function(msg) {
+	log(LOG_DEBUG, msg);
+	client.socket.close();
+	exit();
+}
+
+var getSession = function(un) {
+	var fn = format("%suser/%04d.web", system.data_dir, un);
+	if(!file_exists(fn))
+		return false;
+	var f = new File(fn);
+	if(!f.open("r"))
+		return false;
+	var session = f.iniGetObject();
+	f.close();
+	return session;
+}
+
+// Obfuscated lazy port of an unfinished node.js rlogin client I made quite some time ago.
+// It does what it needs to do.  Unless it doesn't - in which case, replace it with something better.
+var RLogin=function(e){var n=this;const t=24,i=13,s=17,o=19,c=10;var r=[],p=[],u={connected:!1,cooked:!0,suspendInput:!1,suspendOutput:!1,watchForClientEscape:!0,clientHasEscaped:!1},d={rows:24,columns:80,pixelsX:640,pixelsY:480,clientEscape:"~"},f={DOT:n.disconnect,EOT:n.disconnect,SUB:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=u.suspendInput},EOM:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=!1}};this.__defineGetter__("connected",function(){return u.connected}),this.__defineSetter__("connected",function(e){"boolean"!=typeof e||e||n.disconnect()}),this.__defineGetter__("rows",function(){return d.rows}),this.__defineSetter__("rows",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'rows' setting "+e;d.rows=e}),this.__defineGetter__("columns",function(){return d.columns}),this.__defineSetter__("columns",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'columns' setting "+e;d.columns=e}),this.__defineGetter__("pixelsX",function(){return d.pixelsX}),this.__defineSetter__("pixelsX",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsX' setting "+e;d.pixelsX=e}),this.__defineGetter__("pixelsY",function(){return d.pixelsY}),this.__defineSetter__("pixelsY",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsY' setting "+e;d.pixelsY=e}),this.__defineGetter__("clientEscape",function(){return d.clientEscape}),this.__defineSetter__("clientEscape",function(e){if("string"!=typeof e||1!=e.length)throw"RLogin: Invalid 'clientEscape' setting "+e;d.clientEscape=e});var a=new Socket,l=function(){if(!(a.nread<1)){for(var e=[];a.nread>0;)e.push(a.recvBin(1));if(!u.connected)if(0==e[0]){if(u.connected=!0,!(e.length>1))return;e=e.slice(1)}else n.disconnect();u.suspendOutput||(r=r.concat(e))}};this.send=function(e){if(!u.connected)throw"RLogin.send: not connected.";if(u.suspendInput)throw"RLogin.send: input has been suspended.";"string"==typeof e&&(e=e.split("").map(function(e){return ascii(e)}));for(var n=[],r=0;r<e.length;r++)u.watchForClientEscape&&e[r]==d.clientEscape.charCodeAt(0)?(u.watchForClientEscape=!1,u.clientHasEscaped=!0):u.clientHasEscaped?(u.clientHasEscaped=!1,"undefined"!=typeof f[e[r]]&&f[e[r]]()):!u.cooked||e[r]!=s&&e[r]!=o?((r>0&&e[r-1]==i&&e[r]==c||e[r]==t)&&(u.watchForClientEscape=!0),n.push(e[r])):u.suspendOutput==(e[r]==o);p=p.concat(n)},this.receive=function(){return r.splice(0,r.length)},this.addClientEscape=function(e,n){if("string"!=typeof e&&"number"!=typeof e||"string"==typeof e&&e.length>1||"function"!=typeof n)throw"RLogin.addClientEscape: invalid arguments.";f[e.charCodeAt(0)]=n},this.connect=function(){if("number"!=typeof e.port||"string"!=typeof e.host)throw"RLogin: invalid host or port argument.";if("string"!=typeof e.clientUsername)throw"RLogin: invalid clientUsername argument.";if("string"!=typeof e.serverUsername)throw"RLogin: invalid serverUsername argument.";if("string"!=typeof e.terminalType)throw"RLogin: invalid terminalType argument.";if("number"!=typeof e.terminalSpeed)throw"RLogin: invalid terminalSpeed argument.";if(!a.connect(e.host,e.port))throw"RLogin: Unable to connect to server.";for(a.sendBin(0,1),a.send(e.clientUsername),a.sendBin(0,1),a.send(e.serverUsername),a.sendBin(0,1),a.send(e.terminalType+"/"+e.terminalSpeed),a.sendBin(0,1);a.is_connected&&a.nread<1;)mswait(5);l()},this.cycle=function(){if(l(),!(u.suspendInput||p.length<1))for(;p.length>0;)a.sendBin(p.shift(),1)},this.disconnect=function(){a.close(),u.connected=!1}};
+
+try {
+
+	wss = new WebSocketProxy(client);
+
+	if(typeof wss.headers["Cookie"] == "undefined")
+		err("No cookie from WebSocket client.");
+
+	var cookie = wss.headers["Cookie"].split("=");
+	if(cookie[0] != "synchronet" || cookie.length < 2)
+		err("Invalid cookie from WebSocket client.");
+
+	cookie = cookie[1].split(",");
+	cookie[0] = parseInt(cookie[0]);
+	if(cookie.length < 2 || isNaN(cookie[0]) || cookie[0] < 1 || cookie[0] > system.lastuser)
+		err("Invalid cookie from WebSocket client.");
+
+	var usr = new User(cookie[0]);
+	var session = getSession(usr.number);
+	if(!session)
+		err("Unable to read web session file for user #" + usr.number);
+	if(cookie[1] != session.key)
+		err("Session key mismatch for user #" + usr.number);
+	if(typeof session.xtrn != "string" || typeof xtrn_area.prog[session.xtrn] == "undefined")
+		err("Invalid external program code.");
+
+	var f = new File(file_cfgname(system.ctrl_dir, 'sbbs.ini'));
+	if(!f.open("r"))
+		err("Unable to open sbbs.ini.");
+	var ini = f.iniGetObject("BBS");
+	f.close();
+
+	rlogin = new RLogin(
+		{	'host' : system.inet_addr,
+			'port' : ini.RLoginPort,
+			'clientUsername' : usr.security.password,
+			'serverUsername' : usr.alias,
+			'terminalType' : session.xtrn,
+			'terminalSpeed' : 115200
+		}
+	);
+	rlogin.connect();
+	log(LOG_DEBUG, usr.alias + " logged on via RLogin for " + session.xtrn);
+
+	while(client.socket.is_connected && rlogin.connected) {
+
+		wss.cycle();
+		rlogin.cycle();
+
+		var send = rlogin.receive();
+		if(send.length > 0)
+			wss.send(send);
+
+		while(wss.data_waiting) {
+			var data = wss.receiveArray();
+			rlogin.send(data);
+		}
+
+	}
+
+} catch(err) {
+
+	log(err);
+
+} finally {
+	rlogin.disconnect();
+	client.socket.close();
+}
\ No newline at end of file
diff --git a/text/synch.ans b/text/synch.ans
new file mode 100644
index 0000000000..fada940771
--- /dev/null
+++ b/text/synch.ans
@@ -0,0 +1,21 @@
+
+   ��   �
+  �  �ܰ�  ��gj/��
+synchronet   �� �   �  ߲���   ��   ��
+� �    ������    ������  �������� �������� ���������   �  ����
+    ��   �������۱�  ������� ��� ���� ��� ��������   ������  ������ ��
+   ��   ��������۲� ����� ����� �������۲� �� �����������۲� ��۰ ������
+    �  ������� ����� �� � ������� ��������  ���������� ����� ��۲��������� �
+   ���� ��������  ������� �������  � ��۲� ����������� ���۱������������ � ��
+  ��� ����� ������ ����� �����������  ��� ������ ���� � ��������۲� ��������
+ ������� ��� � ������������۲����������������۱  �����  �����  ����� ����
+  ��߲��������� �� ��������۱ ���� ���۲�� ������ �� ܱ� ����������� ���
+  �����۲��� ����� �����������  ��۱�� � �� ����۲����������������
+    ��  ���������� ���� ���� ��������� ����� � ��������  ��� � ����� �
+�  �  ������������ ������  � � ���� ����� � ���   ��� � �����
+   � ��  ���۲�����   �� �߰����    � �
+���� �ݰ�   bbs software
+����  �
+����
+��
+�
diff --git a/web/lib/auth.js b/web/lib/auth.js
new file mode 100644
index 0000000000..def3f73318
--- /dev/null
+++ b/web/lib/auth.js
@@ -0,0 +1,184 @@
+load("sbbsdefs.js");
+load(system.exec_dir + "../web/lib/init.js");
+
+var randomString = function(length) {
+	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split("");
+	var str = '';
+	for (var i = 0; i < length; i++)
+		str += chars[Math.floor(Math.random() * chars.length)];
+	return str;
+}
+
+var getSession = function(un) {
+	var fn = format("%suser/%04d.web", system.data_dir, un);
+	if(!file_exists(fn))
+		return false;
+	var f = new File(fn);
+	if(!f.open("r"))
+		return false;
+	var session = f.iniGetObject();
+	f.close();
+	return session;
+}
+
+var getSessionValue = function(un, key) {
+	var session = getSession(un);
+	if(!session || typeof session[key] == "undefined")
+		return null;
+	return session[key];
+}
+
+var setSessionValue = function(un, key, value) {
+	var fn = format("%suser/%04d.web", system.data_dir, un);
+	var f = new File(fn);
+	f.open(f.exists ? 'r+' : 'w+');
+	f.iniSetValue(null, key, value);
+	f.close();
+}
+
+var setCookie = function(usr, sessionKey) {
+	if(usr instanceof User && usr.number > 0) {
+		set_cookie(
+			'synchronet',
+			usr.number + ',' + sessionKey,
+			(time() + settings.timeout),
+			http_request.host.replace(/\:\d*/g, ""),
+			"/"
+		);
+		setSessionValue(usr.number, 'key', sessionKey);
+	}
+}
+
+var validateSession = function(cookies) {
+
+	for(var c in cookies) {
+
+		if(cookies[c].search(/^\d+,\w+$/) < 0)
+			continue;
+
+		var cookie = cookies[c].split(',');
+
+		try {
+			var usr = new User(cookie[0]);
+			if(usr.number < 1)
+				throw "Invalid user number " + cookie[0] + " in cookie.";
+		} catch(err) {
+			log(LOG_DEBUG, err);
+			continue;
+		}
+
+		var session = getSession(usr.number);
+		if(typeof session != "object")
+			continue;
+		if(typeof session.key != "string" || session.key != cookie[1])
+			continue;
+
+		authenticate(usr.alias, usr.security.password);
+		setCookie(usr, session.key);
+		break;
+
+	}
+
+}
+
+var destroySession = function(cookies) {
+
+	for(var c in cookies) {
+
+		if(cookies[c].search(/^\d+,\w+$/) < 0)
+			continue;
+
+		var cookie = cookies[c].split(',');
+
+		try {
+			
+			var usr = new User(cookie[0]);
+			if(usr.number < 1)
+				throw "Invalid user number " + cookie[0] + " in cookie.";
+
+			var session = getSession(usr.number);
+			if(typeof session != "object")
+				throw "Invalid session for user #" + usr.number;
+
+			if(session.key != cookie[1])
+				throw "Invalid session key for user #" + user.number;
+
+			set_cookie(
+				'synchronet',
+				usr.number + ',' + session.key,
+				(time() - settings.timeout),
+				http_request.host.replace(/\:\d*/g, ""),
+				"/"
+			);
+
+			var fn = format("%suser/%04d.web", system.data_dir, usr.number);
+			file_remove(fn);
+
+			break;
+
+		} catch(err) {
+
+			log("Error destroying session: " + err + ", cookie: " + cookies[c]);
+
+		}
+
+	}
+
+}
+
+var authenticate = function(alias, password) {
+	var un = system.matchuser(alias);
+	if(un < 1)
+		return false;
+	var usr = new User(un);
+	if(usr.settings&USER_DELETED)
+		return false;
+	if(usr.security.password.toUpperCase() != password.toUpperCase())
+		return false;
+	login(usr.alias, usr.security.password.toUpperCase());
+	return usr;
+}
+
+// If someone is trying to log in
+if(	typeof http_request.query.username != "undefined"
+	&& 
+	http_request.query.username[0].length <= LEN_ALIAS
+	&&
+	typeof http_request.query.password != "undefined"
+	&&
+	http_request.query.password[0].length <= LEN_PASS
+) {
+	var usr = authenticate(
+		http_request.query.username[0],
+		http_request.query.password[0]
+	);
+	if(usr instanceof User)
+		setCookie(usr, randomString(512));
+
+// If they have a cookie
+} else if(
+	typeof http_request.cookie.synchronet != "undefined"
+	&&
+	http_request.cookie.synchronet.some(Function('e', 'return(e.search(/^\\d+,\\w+$/) != -1)'))
+) {
+
+	// Verify & update their session, or log them out if requested
+	if(typeof http_request.query.logout == "undefined")
+		validateSession(http_request.cookie.synchronet);
+	else
+		destroySession(http_request.cookie.synchronet);
+}
+
+// If they haven't authenticated as an actual user yet
+if(user.number == 0) {
+
+	// Try to log them in as the guest user
+	var gn = system.matchuser(settings.guest);
+	if(gn > 0) {
+		var gu = new User(gn);
+		login(gu.alias, gu.security.password);
+	} else {
+		// Otherwise just kill the script, for security's sake
+		exit();
+	}
+}
\ No newline at end of file
diff --git a/web/lib/files.js b/web/lib/files.js
new file mode 100644
index 0000000000..34cd386837
--- /dev/null
+++ b/web/lib/files.js
@@ -0,0 +1,38 @@
+load("filedir.js");
+load("file_size.js");
+
+var listLibraries = function() {
+	var libraries = [];
+	file_area.lib_list.forEach(
+		function(library) {
+			if(library.dir_list.length > 0)
+				libraries.push(library);
+		}
+	);
+	return libraries;
+}
+
+var listDirectories = function(library) {
+	var dirs = [];
+	file_area.lib_list[library].dir_list.forEach(
+		function(dir) {
+			var fd = new FileDir(dir);
+			if(fd.files.length < 1)
+				return;
+			dirs.push({'dir' : dir, 'fileCount' : fd.files.length });
+		}
+	);
+	return dirs;
+}
+
+var listFiles = function(dir) {
+	var files = [];
+	var fd = new FileDir(file_area.dir[dir]);
+	fd.files.forEach(
+		function(dirFile) {
+			dirFile.size = file_size_str(file_size(dirFile.fullPath));
+			files.push(dirFile);
+		}
+	);
+	return files;
+}
\ No newline at end of file
diff --git a/web/lib/forum.js b/web/lib/forum.js
new file mode 100644
index 0000000000..594619edb8
--- /dev/null
+++ b/web/lib/forum.js
@@ -0,0 +1,571 @@
+load("sbbsdefs.js");
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "mime-decode.js");
+
+var listGroups = function() {
+	var response = [];
+	msg_area.grp_list.forEach(
+		function(grp) {
+			if(grp.sub_list.length > 0)
+				response.push(grp);
+		}
+	);
+	return response;
+}
+
+// Returns an array of objects of "useful" information about subs
+var listSubs = function(group) {
+	var response = [];
+	msg_area.grp_list[group].sub_list.forEach(
+		function(sub) {
+			response.push(
+				{	'index' : sub.index,
+					'code' : sub.code,
+					'grp_index' : sub.grp_index,
+					'grp_name' : sub.grp_name,
+					'name' : sub.name,
+					'description' : sub.description,
+					'qwk_name' : sub.qwk_name,
+					'qwk_conf' : sub.qwk_conf,
+					'qwk_tagline' : sub.qwknet_tagline,
+					'newsgroup' : sub.newsgroup,
+					'ars' : sub.ars,
+					'read_ars' : sub.read_ars,
+					'can_read' : sub.can_read,
+					'post_ars' : sub.post_ars,
+					'can_post' : sub.can_post,
+					'operator_ars' : sub.operator_ars,
+					'is_operator' : sub.is_operator,
+					'moderated_ars' : sub.moderated_ars,
+					'is_moderated' : sub.is_moderated,
+					'scan_ptr' : sub.scan_ptr,
+					'scan_cfg' : sub.scan_cfg
+				}
+			);
+		}
+	);
+	return response;
+}
+
+var getSubUnreadCount = function(sub) {
+	var ret = {
+		'scanned' : 0,
+		'total' : 0
+	};
+	if(typeof msg_area.sub[sub] == "undefined")
+		return ret;
+	try {
+		var msgBase = new MsgBase(sub);
+		msgBase.open();
+		for(var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
+			var i = msgBase.get_msg_index(m);
+			if(i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP)
+				continue;
+			if(	(	(msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY)
+					&&
+					i.to == crc16_calc(user.alias.toLowerCase())
+					||
+					i.to == crc16_calc(user.name.toLowerCase())
+					||
+					(sub == 'mail' && i.to == crc16_calc(user.number))
+				)
+				||
+				(msg_area.sub[sub].scan_cfg&SCAN_CFG_NEW)
+			) {
+				ret.scanned++;
+			}
+			ret.total++;
+		}
+		msgBase.close();
+	} catch(err) {
+		log(err);
+	}
+	return ret;
+}
+
+var getGroupUnreadCount = function(group) {
+	var ret = {
+		'scanned' : 0,
+		'total' : 0
+	};
+	if(typeof msg_area.grp_list[group] == "undefined")
+		return count;
+	msg_area.grp_list[group].sub_list.forEach(
+		function(sub) {
+			var count = getSubUnreadCount(sub.code);
+			ret.scanned += count.scanned;
+			ret.total += count.total;
+		}
+	);
+	return ret;
+}
+
+var getUnreadInThread = function(sub, thread) {
+	var count = 0;
+	thread.messages.forEach(
+		function(header) {
+			if(header.number > msg_area.sub[sub].scan_ptr)
+				count++;
+		}
+	);
+	return count;
+}
+
+var getMailUnreadCount = function() {
+	var count = 0;
+	var msgBase = new MsgBase('mail');
+	msgBase.open();
+	for(var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
+		var index = msgBase.get_msg_header(m);
+		if(index === null)
+			continue;
+		if(index.to_ext != user.number)
+			continue;
+		if(index.attr&MSG_READ)
+			continue;
+		if(index.attr&MSG_DELETE)
+			continue;
+		count++;
+	}
+	msgBase.close();
+	return count;
+}
+
+var getMailHeaders = function(sent, ascending) {
+	if(typeof sent != "undefined" && sent && user.security.restrictions&UFLAG_K)
+		return []; // They'll just see nothing.  Provide actual feedback?  Does anyone use REST K?
+	var headers = [];
+	var msgBase = new MsgBase('mail');
+	if(!msgBase.open())
+		return headers;
+	for(var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
+		var h = msgBase.get_msg_header(m);
+		if(h === null || h.attr&MSG_DELETE)
+			continue;
+		if((typeof sent != "undefined" && sent) && h.from_ext != user.number)
+			continue;
+		else if((typeof sent == "undefined" || !sent) && h.to_ext != user.number)
+			continue;
+		headers.push(h);
+	}
+	msgBase.close();
+	if(typeof ascending == "undefined" || !ascending)
+		headers.reverse();
+	return headers;
+}
+
+var mimeDecode = function(header, body, code) {
+	var ret = {
+		'type' : "",
+		'body' : [],
+		'inlines' : [],
+		'attachments' : []
+	};
+	var msg = mime_decode(header, body, code);
+	if(typeof msg.inlines != "undefined") {
+		msg.inlines.forEach(
+			function(inline) {
+				ret.inlines.push(
+					format(
+						'<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;cid=%s" target="_blank">%s</a>',
+						code, header.number, inline, inline
+					)
+				);
+			}
+		);
+	}
+	if(typeof msg.attachments != "undefined") {
+		msg.attachments.forEach(
+			function(attachment) {
+				ret.attachments.push(
+					format(
+						'<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;filename=%s" target="_blank">%s</a>',
+						code, header.number, attachment, attachment
+					)
+				);
+			}
+		);
+	}
+	ret.type = msg.type;
+	ret.body = msg.body;
+	return ret;
+}
+
+var getMailBody = function(number) {
+
+	var ret = {
+		'type' : "",
+		'body' : "",
+		'inlines' : [],
+		'attachments' : []
+	};
+
+	number = Number(number);
+	if(isNaN(number) || number < 0)
+		return ret;
+
+	var msgBase = new MsgBase('mail');
+	if(!msgBase.open())
+		return ret;
+	var header = msgBase.get_msg_header(number);
+	if(header !== null && (header.to_ext == user.number || header.from_ext == user.number)) {
+		var body = msgBase.get_msg_body(false, number, header);
+		if(header.to_ext == user.number && (header.attr^MSG_READ)) {
+			header.attr|=MSG_READ;
+			msgBase.put_msg_header(false, number, header);
+		}
+	}
+	msgBase.close();
+	if(typeof body == "undefined" || body === null)
+		return ret;
+
+	var decoded = mimeDecode(header, body, "mail");
+	ret.type = decoded.type;
+	ret.body = formatMessage(decoded.body);
+	ret.inlines = decoded.inlines;
+	ret.attachments = decoded.attachments;
+
+	return ret;
+}
+
+// Returns the user's signature, or an empty String
+var getSignature = function() {
+	var fn = format("%s/user/%04d.sig", system.data_dir, user.number);
+	if(!file_exists(fn))
+		return "";
+	var f = new File(fn);
+	f.open("r");
+	var signature = f.read();
+	f.close();
+	return signature;
+}
+
+// Post a messge to 'sub'
+// Called by postNew/postReply, not directly
+var postMessage = function(sub, header, body) {
+	var ret = false;
+	if(	user.alias == settings.guest
+		||
+		typeof msg_area.sub[sub] == "undefined"
+		||
+		!msg_area.sub[sub].can_post
+		||
+		typeof header.to != "string"
+		||
+		header.to == ""
+		||
+		typeof header.from != "string"
+		||
+		typeof header.subject != "string"
+// This could be a reply to a message with no subject, apparently
+//		||
+//		header.subject == ""
+		||
+		typeof body != "string"
+		||
+		body == ""
+	) {
+		return ret;
+	}
+	try {
+		var msgBase = new MsgBase(sub);
+		msgBase.open();
+		ret = msgBase.save_msg(header, body);
+		msgBase.close();
+	} catch(err) {
+		log(err);
+	}
+	return ret;
+}
+
+// Post a new (non-reply) message to 'sub'
+var postNew = function(sub, to, subject, body) {
+	if(	typeof sub != "string"
+		||
+		typeof to != "string"
+		||
+		to == ""
+		||
+		typeof subject != "string"
+		||
+		subject == ""
+		||
+		typeof body != "string"
+		||
+		body == ""
+	) {
+		return false;
+	}
+	var header = {
+		'to' : to,
+		'from' : user.alias,
+		'subject' : subject
+	};
+	if(sub == "mail")
+		return postMail(header, body);
+	else
+		return postMessage(sub, header, body);
+}
+
+// Post a message to the mail sub, if this user can do so
+// Called by postNew/postReply, not directly
+var postMail = function(header, body) {
+	// Lazy ARS checks; we could check the *type* of email being sent, I guess.
+	if(user.security.restrictions&UFLAG_E || user.security.restrictions&UFLAG_M)
+		return false;
+	if(typeof header.to != "string" || typeof header.subject != "string" || typeof body != "string")
+		return false;
+	var ret = false;
+	if(user.number < 1 || user.alias == settings.guest)
+		return ret;
+	var na = netaddr_type(header.to);
+	if(na > 0) {
+		header.to_net_type = na;
+		header.to_net_addr = header.to;
+	}
+	var msgBase = new MsgBase('mail');
+	if(msgBase.open()) {
+		ret = msgBase.save_msg(header, body);
+		msgBase.close();
+	}
+	return ret;
+}
+
+// Add a new message to 'sub' in reply to parent message 'pid'
+var postReply = function(sub, body, pid) {
+	var ret = false;
+	if(typeof sub != "string" || typeof body != "string" || typeof pid != "number")
+		return ret;
+	try {
+		var msgBase = new MsgBase(sub);
+		msgBase.open();
+		var pHeader = msgBase.get_msg_header(pid);
+		msgBase.close();
+		if(pHeader === null)
+			return ret;
+		var header = {
+			'to' : pHeader.from,
+			'to_net_addr' : pHeader.from_net_addr,
+			'from' : user.alias,
+			'subject' : pHeader.subject,
+			'thread_back' : pHeader.number
+		};
+		if(sub == 'mail')
+			ret = postMail(header, body);
+		else
+			ret = postMessage(sub, header, body);
+	} catch(err) {
+		log(err);
+	}
+	return ret;
+}
+
+// Delete a message if
+// - This is the mail sub, and the message was sent by or to this user
+// - This is another sub on which the user is an operator
+var deleteMessage = function(sub, number) {
+	number = parseInt(number);
+	if(typeof msg_area.sub[sub] == "undefined" && sub != "mail")
+		return false;
+	var msgBase = new MsgBase(sub);
+	if(!msgBase.open())
+		return false;
+	var header = msgBase.get_msg_header(number);
+	if(header === null)
+		return false;
+	if(sub == 'mail' && (header.to_ext == user.number || header.from_ext == user.number))
+		var ret = msgBase.remove_msg(number);
+	else if(sub != 'mail' && msg_area.sub[sub].is_operator)
+		var ret = msgBase.remove_msg(number);
+	else
+		var ret = false;
+	msgBase.close();
+	return ret;
+}
+
+// Deuce's URL-ifier
+var linkify = function(body) {
+	urlRE=/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
+	body=body.replace(
+		urlRE, 
+		function(str) {
+			var ret=''
+			var p=0;
+			var link=str.replace(/\.*$/, '');
+			var linktext=link;
+			if(link.indexOf('://')==-1)
+				link='mailto:'+link;
+			return('<a class="ulLink" href="'+link+'">'+linktext+'</a>'+str.substr(linktext.length));
+		}
+	);
+	return(body);
+}
+
+// Somewhat modified version of Deuce's "magical quoting stuff" from v3
+var quotify = function(body) {
+
+	var blockquote_start = '<blockquote>';
+	var blockquote_end = '</blockquote>';
+
+	var lines = body.split(/\r?\n/);
+	body = '';
+
+	var quote_depth=0;
+	var prefixes = [];
+
+	for(l in lines) {
+
+		var line_prefix = '';
+		var m = lines[l].match(/^((?:\s?[^\s]{0,3}&gt;\s?)+)/);
+
+		if(m !== null) {
+
+			var new_prefixes = m[1].match(/\s?[^\s]{0,3}&gt;\s?/g);
+			var p;
+			var broken = false;
+
+			line = lines[l];
+			
+			// If the new length is smaller than the old one, close the extras
+			for(p = new_prefixes.length; p < prefixes.length; p++) {
+				if(quote_depth < 1)
+					continue;
+				line_prefix = line_prefix + blockquote_end;
+				quote_depth--;
+			}
+
+			for(p in new_prefixes) {
+				// Remove prefix from start of line
+				line = line.substr(new_prefixes[p].length);
+
+				if(typeof prefixes[p] == "undefined") {
+					/* New depth */
+					line_prefix = line_prefix + blockquote_start;
+					quote_depth++;
+				} else if(broken) {
+					line_prefix = line_prefix + blockquote_start;
+					quote_depth++;
+				} else if(prefixes[p].replace(/^\s*(.*?)\s*$/,"$1") != new_prefixes[p].replace(/^\s*(.*?)\s*$/,"$1")) {
+					// Close all remaining old prefixes and start one new one
+					var o;
+					for(o = p; o < prefixes.length && o < new_prefixes.length; o++) {
+						if(quote_depth > 0) {
+							line_prefix = blockquote_end + line_prefix;
+							quote_depth--;
+						}
+					}
+					line_prefix = blockquote_start + line_prefix;
+					quote_depth++;
+					broken = true;
+				}
+			}
+
+			prefixes = new_prefixes.slice();
+			line = line_prefix + line;
+
+		} else {
+
+			for(p = 0; p < prefixes.length; p++) {
+				if(quote_depth < 1)
+					continue;
+				line_prefix = line_prefix + blockquote_end;
+				quote_depth--;
+			}
+			prefixes = [];
+			line = line_prefix + lines[l];
+
+		}
+
+		body = body + line + "\r\n";
+
+	}
+
+	if(quote_depth != 0) {
+		for(;quote_depth > 0; quote_depth--)
+			body += blockquote_end;
+	}
+
+	return body.replace(/\<\/blockquote\>\r\n<blockquote\>/g, "\r\n");
+
+}
+
+// Format message body for the web
+var formatMessage = function(body, ansi) {
+
+	// Workaround for html_encode(body, true, false, false, false);
+	// which causes a crash if body is empty
+	if(body == "")
+		return body;
+
+	if(typeof ansi == "boolean" && ansi) {
+
+		body = html_encode(body, true, false, true, true);
+		body = body.replace(/\r?\n+(<\/span>)?$/,'$1');
+		body = linkify(body);
+
+		// Get the last line
+		var body_m = body.match(/\n([^\n]*)$/);
+		if(body_m != null) {
+			body = '<pre>'+body;
+			body_m[1] = body_m[1].replace(/&[^;]*;/g,".");
+			body_m[1] = body_m[1].replace(/<[^>]*>/g,"");
+			var lenremain = 80 - body_m[1].length;
+			while(lenremain > 0) {
+				body += '&nbsp;';
+				lenremain--;
+			}
+			body += '</pre>';
+		} else {
+			/* If we couldn't get the last line, add a line of 80 columns */
+			var line = "";
+			for(n = 0; n < 80; n++)
+				line += "&nbsp;";
+			body = '<pre>' + body + line + "</pre>";
+		}
+
+	} else {
+
+		// Strip CTRL-A
+		body = body.replace(/\1./g,'');
+		// Strip ANSI
+		body = body.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g,'');
+		body = body.replace(/\x1b[\x40-\x7e]/g,'');
+		// Strip unprintable control chars (NULL, BEL, DEL, ESC)
+		body = body.replace(/[\x00\x07\x1b\x7f]/g,'');
+
+		// Format for the web
+		body = word_wrap(body, body.length);
+		body = html_encode(body, true, false, false, false);
+		body = quotify(body);
+		body = linkify(body);
+		body = body.replace(/\r\n$/,'');
+		body = body.replace(/(\r?\n)/g, "<br>$1");
+
+	}
+
+	return body;
+
+}
+
+var setScanCfg = function(sub, cfg) {
+
+	var opts = [
+		0,
+		SCAN_CFG_NEW,
+		SCAN_CFG_YONLY
+	];
+
+	if(typeof msg_area.sub[sub] == "undefined")
+		return false;
+
+	cfg = parseInt(cfg);
+	if(isNaN(cfg) || cfg < 0 || cfg > 2)
+		return false;
+
+	if(cfg == 2)
+		opts[cfg]|=SCAN_CFG_NEW;
+
+	msg_area.sub[sub].scan_cfg = opts[cfg];
+	return true;
+
+}
\ No newline at end of file
diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
new file mode 100644
index 0000000000..cea9a8b7ec
--- /dev/null
+++ b/web/lib/ftelnet.js
@@ -0,0 +1,14 @@
+var f = new File(file_cfgname(system.ctrl_dir, "services.ini"));
+if(!f.open("r"))
+	exit();
+var webSocket = f.iniGetObject("WebSocket");
+var webSocketRLogin = f.iniGetObject("WebSocketRLogin");
+f.close();
+
+var getSplash = function() {
+	var f = new File(settings.ftelnet_splash);
+	f.open("r");
+	var splash = base64_encode(f.read());
+	f.close();
+	return splash;
+}
\ No newline at end of file
diff --git a/web/lib/init.js b/web/lib/init.js
new file mode 100644
index 0000000000..53d1c27935
--- /dev/null
+++ b/web/lib/init.js
@@ -0,0 +1,52 @@
+load("modopts.js");
+
+var settings = get_mod_options("web");
+
+// Paths
+settings.web_directory = fullpath(
+	backslash(
+		typeof settings.web_directory == "undefined" ? "../web" : settings.web_directory
+	)
+);
+settings.web_root = backslash(settings.web_directory + "root/");
+settings.web_lib = backslash(settings.web_directory + "lib/");
+
+// Guest
+if(typeof settings.guest == "undefined")
+	settings.guest = "Guest";
+if(system.matchuser(settings.guest) == 0)
+	exit();
+
+// Timeout
+if(typeof settings.timeout != "number")
+	settings.timeout = 43200;
+
+// Registration
+if(typeof settings.user_registration != "boolean") {
+	settings.user_registration = false;
+} else {
+
+	if(typeof settings.minimum_password_length != "number")
+		settings.minimum_password_length = 4;
+
+	if(typeof settings.email_validation != "boolean")
+		settings.email_validation = true;
+
+	if(typeof settings.email_validation_level != "number")
+		settings.email_validation_level = 50;
+
+}
+
+if(typeof settings.xtrn_sections == "string") {
+	settings.xtrn_sections = settings.xtrn_sections.split(",").filter(
+		function(section) {
+			if(typeof xtrn_area.sec[section] == "undefined")
+				return false;
+			if(!xtrn_area.sec[section].can_access)
+				return false;
+			if(xtrn_area.sec_list[xtrn_area.sec[section].index].prog_list.length < 1)
+				return false;
+			return true;	
+		}
+	);
+}
\ No newline at end of file
diff --git a/web/lib/mime-decode.js b/web/lib/mime-decode.js
new file mode 100644
index 0000000000..4865315bc1
--- /dev/null
+++ b/web/lib/mime-decode.js
@@ -0,0 +1,283 @@
+/* $Id: mime_decode.ssjs,v 1.22 2009/05/06 21:54:05 deuce Exp $ */
+
+function regex_escape(str)
+{
+    str=str.replace(/([\\\^\$\*\+\?\.\(\)\{\}\[\]])/g,"\\$1");
+    return(str);
+}
+
+function count_attachments(hdr, body)
+{
+	var Message=new Array;
+	var CT;
+	var TE;
+	var attach=0;
+
+	if(hdr==undefined || body==undefined || hdr.field_list==undefined)
+		return(0);
+	for(head in hdr.field_list) {
+		if(hdr.field_list[head].data.search(/content-type:/i)!=-1) {
+			CT=hdr.field_list[head].data;
+		}
+		else if(hdr.field_list[head].data.search(/content-transfer-encoding:/i)!=-1) {
+			TE=hdr.field_list[head].data;
+		}
+	}
+
+	if(CT==undefined)
+		return(0);
+
+	if(CT.search(/multipart\/[^\s;]*/i)!=-1) {
+		var bound=CT.match(/;[\s\r\n]*boundary="{0,1}([^";\r\n]*)"{0,1}/i);
+		if(bound==undefined)
+			return(attach);
+		bound[1]=regex_escape(bound[1]);
+		re=new RegExp ("--"+bound[1]+"-{0,2}","");
+		msgbits=body.split(re);
+		/* Search for attachments/inlined */
+		for(bit in msgbits) {
+			var pieces=msgbits[bit].split(/\r?\n\r?\n/);
+			var disp=pieces[0].match(/content-disposition:\s+(?:attachment|inline)[;\s]*filename="?([^";\r\n]*)"?/i);
+			if(disp!=undefined) {
+				attach++;
+			}
+		}
+	}
+
+	return(attach);
+}
+
+function mime_decode(hdr, body, sub)
+{
+	var Message=new Array;
+	var CT;
+	var TE;
+	var undef;
+
+	if(hdr==undefined || body==undefined || hdr.field_list==undefined) {
+		Message.type="plain";
+		Message.body=decode_body(TE,undef,body);
+		return(Message);
+	}
+	for(head in hdr.field_list) {
+		if(hdr.field_list[head].data.search(/content-type:/i)!=-1) {
+			CT=hdr.field_list[head].data;
+		}
+		else if(hdr.field_list[head].data.search(/content-transfer-encoding:/i)!=-1) {
+			TE=hdr.field_list[head].data;
+		}
+	}
+	if(CT==undefined) {
+		Message.type="plain";
+		Message.body=decode_body(TE,undef,body);
+		return(Message);
+	}
+	if(CT.search(/multipart\/[^\s;]*/i)!=-1) {
+		var bound=CT.match(/;[\s\r\n]*boundary="{0,1}([^";\r\n]*)"{0,1}/i);
+		if(bound!=undefined) {
+			bound[1]=regex_escape(bound[1]);
+			re=new RegExp ("--"+bound[1]+"-{0,2}");
+			msgbits=body.split(re);
+			/* Search for attachments/inlined */
+			for(bit in msgbits) {
+				var pieces=msgbits[bit].split(/\r?\n\r?\n/);
+				var disp=pieces[0].match(/content-disposition:\s+(?:attachment|inline)[;\s]*filename="?([^";\r\n]*)"?/i);
+				if(disp!=undefined) {
+					/* Attachment */
+					if(Message.attachments==undefined)
+						Message.attachments=new Array;
+					Message.attachments.push(disp[1]);
+				}
+				disp=pieces[0].match(/content-id:\s+\<?([^\<\>;\r\n]*)\>?/i);
+				if(disp!=undefined) {
+					/* Inline Attachment */
+					if(Message.inlines==undefined)
+						Message.inlines=new Array;
+					Message.inlines.push(disp[1]);
+				}
+			}
+			/* Search for HTML encoded bit */
+			for(bit in msgbits) {
+				var pieces=msgbits[bit].split(/\r?\n\r?\n/);
+				var pheads=pieces[0];
+				if(pheads==undefined)
+					continue;
+				var content=pieces.slice(1).join('');
+				if(content==undefined)
+					continue;
+				if(pheads.search(/content-type: text\/html/i)!=-1) {
+					Message.body=decode_body(TE,pheads,content);
+					if(Message.inlines!=undefined) {
+						for(il in Message.inlines) {
+							var path=http_request.virtual_path;
+							var basepath=path.match(/^(.*\/)[^\/]*$/);
+							re=new RegExp("cid:("+regex_escape(Message.inlines[il])+")","ig");
+							Message.body=Message.body.replace(re,basepath[1]+"api/attachments.ssjs?sub="+sub+"&amp;msg="+hdr.number+"&amp;cid=$1");
+						}
+					}
+					Message.type="html";
+					return(Message);
+				}
+			}
+			/* Search for plaintext bit */
+			for(bit in msgbits) {
+				var pieces=msgbits[bit].split(/\r?\n\r?\n/);
+				var pheads=pieces[0];
+				var content=pieces.slice(1).join('');
+				if(content==undefined)
+					continue;
+				if(pheads.search(/content-type: text\/plain/i)!=-1) {
+					Message.body=decode_body(TE,pheads,content);
+					Message.type="plain";
+					return(Message);
+				}
+			}
+		}
+	}
+
+	if(CT.search(/text\/html/i)!=-1) {
+		Message.type="html";
+		Message.body=decode_body(TE,undef,body);
+		return(Message);
+	}
+
+	Message.type="plain";
+	Message.body=body;
+	return(Message);
+}
+
+function decode_body(TE, heads, body)
+{
+	var tmp;
+
+	if(heads!=undefined && heads != "") {
+		tmp=heads.match(/content-transfer-encoding: ([^;\r\n]*)/i);
+		if(tmp!=undefined)
+			tmp=tmp[1];
+		else {
+			if(TE!=undefined) {
+				tmp=TE.match(/content-transfer-encoding: ([^;\r\n]*)/i);
+				if(tmp!=undefined)
+					tmp=tmp[1];
+			}
+		}
+	}
+	else {
+		if(TE!=undefined) {
+			tmp=TE.match(/content-transfer-encoding: ([^;\r\n]*)/i);
+			if(tmp!=undefined)
+				tmp=tmp[1];
+		}
+		else
+			tmp="";
+	}
+	
+	if(tmp==undefined)
+		tmp="";
+	if(tmp.search(/quoted-printable/i)!=-1) {
+		body=body.replace(/\=(\r{0,1}\n)/g,"$1");
+		body=body.replace(/\=([A-F0-9]{2})/ig,function (str,p1,offset,s)
+			{
+				var i=parseInt(p1,16);
+				if(i==NaN || i==undefined)
+					return('='+p1);
+				return ascii(i);
+			}
+		);
+		return body;
+	}
+	if(tmp.search(/base64/i)!=-1) {
+		body=body.replace(/[^A-Za-z0-9\+\/\=]/g,'');
+		return base64_decode(body);
+	}
+
+	return body;
+}
+
+function mime_get_attach(hdr, body, filename)
+{
+	var Message=new Array;
+	var CT;
+	var TE;
+	var undef;
+
+	for(head in hdr.field_list) {
+		if(hdr.field_list[head].data.search(/content-type:/i)!=-1) {
+			CT=hdr.field_list[head].data;
+		}
+		else if(hdr.field_list[head].data.search(/content-transfer-encoding:/i)!=-1) {
+			TE=hdr.field_list[head].data;
+		}
+	}
+	if(CT==undefined) {
+		return(undefined);
+	}
+	if(CT.search(/multipart\/[^\s;]*/i)!=-1) {
+		var bound=CT.match(/;[\s\r\n]*boundary="{0,1}([^";\r\n]*)"{0,1}/i);
+		if(bound==undefined)
+			return(undefined);
+		bound[1]=regex_escape(bound[1]);
+		re=new RegExp ("--"+bound[1]+"-{0,2}");
+		msgbits=body.split(re);
+		/* Search for attachments */
+		for(bit in msgbits) {
+			var pieces=msgbits[bit].split(/\r+\n\r+\n/);
+			var disp=pieces[0].match(/content-disposition:\s+(?:attachment|inline)[;\s]*filename="?([^";\r\n]*)"?/i);
+			if(disp==undefined)
+				continue;
+			if(disp[1]==filename) {
+				var contyp=pieces[0].match(/content-type:\s*([^\r\n]*)/i);
+				var content=pieces.slice(1).join('');
+				if(contyp!=undefined && contyp[0]!=undefined)
+					Message.content_type=contyp[1];
+				Message.body=decode_body(undefined,pieces[0],content);
+				return(Message);
+			}
+		}
+	}
+	return(undefined);
+}
+
+function mime_get_cid_attach(hdr, body, cid)
+{
+	var Message=new Array;
+	var CT;
+	var TE;
+	var undef;
+
+	for(head in hdr.field_list) {
+		if(hdr.field_list[head].data.search(/content-type:/i)!=-1) {
+			CT=hdr.field_list[head].data;
+		}
+		else if(hdr.field_list[head].data.search(/content-transfer-encoding:/i)!=-1) {
+			TE=hdr.field_list[head].data;
+		}
+	}
+	if(CT==undefined) {
+		return(undefined);
+	}
+	if(CT.search(/multipart\/[^\s;]*/i)!=-1) {
+		var bound=CT.match(/;[\s\r\n]*boundary="{0,1}([^";\r\n]*)"{0,1}/i);
+		if(bound==undefined)
+			return(undefined);
+		bound[1]=regex_escape(bound[1]);
+		re=new RegExp ("--"+bound[1]+"-{0,2}");
+		msgbits=body.split(re);
+		/* Search for attachments */
+		for(bit in msgbits) {
+			var pieces=msgbits[bit].split(/\r?\n\r?\n/);
+			var disp=pieces[0].match(/content-id:\s+<?([^\<\>;\r\n]*)>?/i);
+			if(disp==undefined)
+				continue;
+			if(disp[1]==cid) {
+				var contyp=pieces[0].match(/content-type:\s*([^\r\n]*)/i);
+				var content=pieces.slice(1).join('');
+				if(contyp!=undefined && contyp[0]!=undefined)
+					Message.content_type=contyp[1];
+				Message.body=decode_body(undefined,pieces[0],content);
+				return(Message);
+			}
+		}
+	}
+	return(undefined);
+}
diff --git a/web/lib/pages.js b/web/lib/pages.js
new file mode 100644
index 0000000000..31f6a3176b
--- /dev/null
+++ b/web/lib/pages.js
@@ -0,0 +1,151 @@
+var getWebCtrl = function() {
+	if(!file_exists(settings.web_root + "pages/webctrl.ini"))
+		return false;
+	var f = new File(settings.web_root + "pages/webctrl.ini");
+	if(!f.open("r")) {
+		log("Unable to open pages/webctrl.ini");
+		exit();
+	}
+	var ini = f.iniGetAllObjects();
+	f.close();
+	return ini;
+}
+
+var webCtrlTest = function(ini, filename) {
+	var ret = true;
+	for(var i = 0; i < ini.length; i++) {
+		if(!wildmatch(false, filename, ini[i].name))
+			continue;
+		if(	typeof ini[i].AccessRequirements == "undefined"
+			||
+			user.compare_ars(ini[i].AccessRequirements)
+		) {
+			continue;
+		}
+		ret = false;
+		break;
+	}
+	return ret;
+}
+
+var webCtrlFilter = function(pages) {
+	var ini = getWebCtrl();
+	if(typeof ini == "boolean" && !ini)
+		return pages;
+	pages = pages.filter(
+		function(page) {
+			return webCtrlTest(ini, page.page);
+		}
+	);
+	return pages;
+}
+
+var getPages = function(primary) {
+
+	if(typeof primary == "undefined")
+		var wc = "*";
+	else if(!primary)
+		var wc = "*_*";
+	else
+		var wc = "*-*";
+
+	var pages = [];
+	var d = directory(settings.web_root + "pages/" + wc);
+	d.forEach(
+		function(item) {
+			if(file_isdir(item))
+				return;
+			var fn = file_getname(item);
+			var title = getPageTitle(item);
+			if(typeof title == "undefined" || title.search(/^HIDDEN/) == 0)
+				return;
+			pages.push(
+				{	'page' : fn,
+					'title' : title
+				}
+			);
+		}
+	);
+	return webCtrlFilter(pages);
+
+}
+
+var getPageTitle = function(file) {
+
+	var ext = file_getext(file).toUpperCase();
+
+	var f = new File(file);
+	f.open('r');
+	var i = f.readAll();
+	f.close();
+
+	if(ext == ".JS" || (ext == ".SSJS" && file.search(/\.xjs\.ssjs$/i)==-1)) {
+		var title = i[0].replace(/\/\//g, "");
+		return title;
+	}
+
+	if(ext == ".HTML" || ext == ".XJS") {
+		// Seek first comment line in an HTML document
+		for(var j = 0; j < i.length; j++) {
+			var k = i[j].match(/^\<\!\-\-.*\-\-\>$/);
+			if(k === null)
+				continue;
+			var title = k[0].replace(/[\<\!\-+|\-+\>]/g, "");
+			return title;
+		}
+	}
+
+	if(ext == ".TXT")
+		return file_getname(file);
+
+}
+
+var getPage = function(page) {
+
+	var ret = "";
+
+	page = settings.web_root + "pages/" + page;
+
+	if(!file_exists(page))
+		return ret;
+
+	var ext = file_getext(page).toUpperCase();
+
+	if(user.alias != settings.guest) {
+		var title = getPageTitle(page);
+		if(title != "HIDDEN")
+			setSessionValue(user.number, 'action', title);
+	}
+
+	switch(ext) {
+		case ".SSJS":
+			if(ext == ".SSJS" && page.search(/\.xjs\.ssjs$/i) >= 0)
+				break;
+			load(page, true);
+			break;
+		case ".XJS":
+			load(xjs_compile(page), true);
+			break;
+		case ".HTML":
+			var f = new File(page);
+			f.open("r");
+			if(f.is_open) {
+				ret = f.read();
+				f.close();
+			}
+			break;
+		case ".TXT":
+			var f = new File(page);
+			f.open("r");
+			if(f.is_open) {
+				ret = "<pre>" + f.read() + "</pre>";
+				f.close();
+			}
+			break;
+		default:
+			break;
+	}
+
+	return ret;
+
+}
\ No newline at end of file
diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
new file mode 100644
index 0000000000..347aca1330
--- /dev/null
+++ b/web/lib/sidebar.js
@@ -0,0 +1,75 @@
+var getSidebarModules = function() {
+
+	var sidebarModules = [];
+	var d = directory(settings.web_root + "sidebar/*");
+	d.forEach(
+		function(item) {
+			if(file_isdir(item))
+				return;
+			var fn = file_getname(item);
+			// Check webctrl.ini
+			sidebarModules.push(fn);
+		}
+	);
+	return sidebarModules;
+
+}
+
+var getSidebarModule = function(module) {
+
+	var ret = "";
+
+	if(!file_exists(module))
+		return ret;
+
+	var ext = file_getext(module).toUpperCase();
+
+	switch(ext) {
+		case ".SSJS":
+			if(ext == ".SSJS" && module.search(/\.xjs\.ssjs$/i) >= 0)
+				break;
+			load(module, true);
+			break;
+		case ".XJS":
+			load(xjs_compile(module), true);
+			break;
+		case ".HTML":
+			var f = new File(module);
+			f.open("r");
+			if(f.is_open) {
+				ret = f.read();
+				f.close();
+			}
+			break;
+		case ".TXT":
+			var f = new File(module);
+			f.open();
+			if(f.is_open) {
+				ret = "<pre>" + f.read() + "</pre>";
+				f.close();
+			}
+			break;
+		default:
+			break;
+	}
+
+	return ret;
+
+}
+
+var writeSidebarModules = function() {
+	var modules = getSidebarModules();
+	write('<ul class="list-group">');
+	modules.forEach(
+		function(module) {
+			if(module.search(/\.xjs\.ssjs$/i) >= 0)
+				return;
+			write('<li class="list-group-item sidebar">');
+			var str = getSidebarModule(settings.web_root + "sidebar/" + module);
+			if(str != "")
+				write(str);
+			write('</li>');
+		}
+	);
+	write('</ul>');
+}
\ No newline at end of file
diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
new file mode 100644
index 0000000000..4d8f4eb0ca
--- /dev/null
+++ b/web/root/api/attachments.ssjs
@@ -0,0 +1,58 @@
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "auth.js");
+load(settings.web_lib + "mime-decode.js");
+
+var barfOut = function(err) {
+	log(err);
+	exit();
+}
+
+if(	typeof http_request.query.sub == "undefined"
+	||
+	(	http_request.query.sub[0] != 'mail'
+		&&
+		typeof msg_area.sub[http_request.query.sub[0]] == "undefined"
+	)
+) {
+	barfOut("Invalid sub.");
+}
+
+var sub = http_request.query.sub[0];
+
+if(typeof http_request.query.msg == "undefined")
+	barfOut("No message number provided.");
+var id = parseInt(http_request.query.msg[0]);
+
+if(typeof http_request.query.cid != "undefined")
+	var cid = http_request.query.cid[0];
+else if(typeof http_request.query.filename != "undefined")
+	var filename = http_request.query.filename[0];
+else
+	barfOut("No attachment specified.");
+
+var msgBase = new MsgBase(sub);
+if(!msgBase.open())
+	barfOut("Unable to open MsgBase " + sub);
+
+var header = msgBase.get_msg_header(false, id);
+if(header === null)
+	barfOut("No such message.");
+if(typeof msgBase.cfg == "undefined" && header.to_ext != user.number)
+	barfOut("Not your message.");
+
+var body = msgBase.get_msg_body(false, id, header);
+if(body === null)
+	barfOut("Cannot read message body!");
+msgBase.close();
+
+if(typeof cid != "undefined")
+	var att = mime_get_cid_attach(header, body, cid);
+else if(typeof filename != "undefined")
+	var att = mime_get_attach(header, body, filename);
+
+if(typeof att != "undefined") {
+	if(typeof att.content_type != "undefined")
+		http_reply.header["Content-Type"] = att.content_type;
+	http_reply.header["Content-Length"] = att.body.length;
+	write(att.body);
+}
\ No newline at end of file
diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
new file mode 100644
index 0000000000..33f9899e27
--- /dev/null
+++ b/web/root/api/auth.ssjs
@@ -0,0 +1,11 @@
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "auth.js");
+
+var response = JSON.stringify(
+	{ 'authenticated' : (user.alias != settings.guest) }
+);
+
+http_reply.header["Content-Type"] = "application/json";
+http_reply.header["Content-Length"] = response.length;
+
+write(response);
\ No newline at end of file
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
new file mode 100644
index 0000000000..2b45fc541b
--- /dev/null
+++ b/web/root/api/files.ssjs
@@ -0,0 +1,65 @@
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "auth.js");
+load(settings.web_lib + "files.js");
+load("filedir.js");
+
+var reply = {};
+
+if(	(http_request.method == "GET" || http_request.method == "POST")
+	&&
+	typeof http_request.query.call != "undefined"
+	&&
+	user.number > 0
+	&&
+	user.alias != settings.guest
+) {
+
+	switch(http_request.query.call[0].toLowerCase()) {
+		case "download-file":
+			reply = false;
+			if(	typeof http_request.query.dir != "undefined"
+				&&
+				typeof file_area.dir[http_request.query.dir[0]] != "undefined"
+				&&
+				file_area.dir[http_request.query.dir[0]].can_download
+				&&
+				typeof http_request.query.file != "undefined"
+			) {
+				var fileDir = new FileDir(file_area.dir[http_request.query.dir[0]]);
+				var file = null;
+				fileDir.files.forEach(
+					function(f) {
+						if(f.name.toLowerCase() != http_request.query.file[0].toLowerCase())
+							return;
+						file = f;
+					}
+				);
+				if(file === null)
+					break;
+				client.socket.send('HTTP/1.0 Status: 200 OK\r\n');
+				client.socket.send('Content-Type: application/octet-stream\r\n');
+				client.socket.send('Content-Disposition: attachment; filename="' + file.name + '";\r\n');
+				client.socket.send('Content-Transfer-Encoding: binary\r\n');
+				client.socket.send('Content-Length: ' + file_size(file.fullPath) + '\r\n');
+				client.socket.send('\r\n');
+				var f = new File(file.fullPath);
+				f.open("r+b");
+				while(!f.eof) {
+					client.socket.sendBin(f.readBin(1), 1);
+				}
+				f.close();
+			}
+			break;
+		default:
+			break;
+	}
+
+}
+
+if(!reply)
+	exit();
+
+reply = JSON.stringify(reply);
+http_reply.header["Content-Type"] = "application/json";
+http_reply.header["Content-Length"] = reply.length;
+write(reply);
\ No newline at end of file
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
new file mode 100644
index 0000000000..8d8ea32e09
--- /dev/null
+++ b/web/root/api/forum.ssjs
@@ -0,0 +1,146 @@
+/*	This script is an interface between HTTP clients and the functions defined
+	in web/lib/forum.js.  A basic check for an authenticated, non-guest user
+	is done here; otherwise all permission checking is done at the function
+	level. */
+
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "auth.js");
+load(settings.web_lib + "forum.js");
+
+var reply = {};
+
+// There must be an API call, and the user must not be a guest or unknown
+if(	(http_request.method == "GET" || http_request.method == "POST")
+	&&
+	typeof http_request.query.call != "undefined"
+	&&
+	user.number > 0
+	&&
+	user.alias != settings.guest
+) {
+
+	switch(http_request.query.call[0].toLowerCase()) {
+
+		case "list-groups":
+			reply = listGroups();
+			break;
+		
+		case "list-subs":
+			if(typeof http_request.query.group != "undefined")
+				reply = listSubs(http_request.query.group[0]);
+			break;
+		
+		case "list-threads":
+			if(typeof http_request.query.sub != "undefined")
+				reply = listThreads(http_request.query.sub[0]);
+			break;
+		
+		case "get-sub-unread-count":
+			if(typeof http_request.query.sub != "undefined") {
+				http_request.query.sub.forEach(
+					function(sub) {
+						reply[sub] = getSubUnreadCount(sub);
+					}
+				);
+			}
+			break;
+		
+		case "get-group-unread-count":
+			if(typeof http_request.query.group != "undefined") {
+				http_request.query.group.forEach(
+					function(group) {
+						reply[group] = getGroupUnreadCount(group);
+					}
+				);
+			}
+			break;
+		
+		case "get-mail-unread-count":
+			reply.count = getMailUnreadCount();
+			break;
+		
+		case "get-mail-body":
+			if(typeof http_request.query.number != "undefined")
+				reply = getMailBody(http_request.query.number[0]);
+			break;
+		
+		case "get-signature":
+			reply.signature = getSignature();
+			break;
+		
+		case "post-reply":
+			if(	typeof http_request.query.sub != "undefined"
+				&&
+				typeof http_request.query.body != "undefined"
+				&&
+				typeof http_request.query.pid != "undefined"
+			) {
+				reply.success = postReply(
+					http_request.query.sub[0],
+					http_request.query.body[0],
+					Number(http_request.query.pid[0])
+				);
+			} else {
+				reply.success = false;
+			}
+			break;
+		
+		case "post":
+			if( typeof http_request.query.sub != "undefined"
+				&&
+				typeof http_request.query.to != "undefined"
+				&&
+				typeof http_request.query.subject != "undefined"
+				&&
+				typeof http_request.query.body != "undefined"
+			) {
+				reply.success = postNew(
+					http_request.query.sub[0],
+					http_request.query.to[0],
+					http_request.query.subject[0],
+					http_request.query.body[0]
+				);
+			} else {
+				reply.success = false;
+			}
+			break;
+		
+		case "delete-message":
+			if( typeof http_request.query.sub != "undefined"
+				&&
+				typeof http_request.query.number != "undefined"
+			) {
+				reply.success = deleteMessage(
+					http_request.query.sub[0],
+					http_request.query.number[0]
+				);
+			} else {
+				reply.success = false;
+			}
+			break;
+
+		case "set-scan-cfg":
+			if(	typeof http_request.query.sub != "undefined"
+				&&
+				typeof http_request.query.cfg != "undefined"
+			) {
+				reply.success = setScanCfg(
+					http_request.query.sub[0],
+					http_request.query.cfg[0]
+				);
+			} else {
+				reply.success = false;
+			}
+			break;
+		
+		default:
+			break;
+			
+	}
+
+}
+
+reply = JSON.stringify(reply);
+http_reply.header["Content-Type"] = "application/json";
+http_reply.header["Content-Length"] = reply.length;
+write(reply);
\ No newline at end of file
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
new file mode 100644
index 0000000000..1d8073f6f4
--- /dev/null
+++ b/web/root/api/register.ssjs
@@ -0,0 +1,180 @@
+load('sbbsdefs.js');
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "/auth.js");
+
+if(user.alias != settings.guest)
+	exit();
+
+if(!settings.user_registration)
+	exit();
+
+var MIN_ALIAS = 1,
+	MIN_REALNAME = 3,
+	MIN_NETMAIL = 6,
+	MIN_LOCATION = 4,
+	MIN_ADDRESS = 6,
+	MIN_PHONE = 3;
+
+
+var reply = {
+	'errors' : [],
+	'userNumber' : 0
+};
+
+var prepUser = {
+	'alias' : "",
+	'handle' : "",
+	'realname' : "",
+	'netmail' : "",
+	'address' : "",
+	'location' : "",
+	'phone' : "",
+	'birthdate' : "",
+	'gender' : "",
+	'password' : ""
+};
+
+var required = function(mask) {
+	return (system.new_user_questions&mask);
+}
+
+var cleanParam = function(param) {
+	if(paramExists(param))
+		return http_request.query[param][0].replace(/[^\x20-\x7E]/g, "");
+	return "";
+}
+
+var paramExists = function(param) {
+	if(	typeof http_request.query[param] != "undefined"
+		&&
+		http_request.query[param][0] != ""
+	) {
+		return true;
+	}
+	return false;
+}
+
+var paramLength = function(param) {
+	if(typeof http_request.query[param] == "undefined")
+		return 0;
+	else if(http_request.query[param][0].replace(" ", "").length < 1)
+		return 0;
+	else if(cleanParam(param).length < 1)
+		return 0;
+	else
+		return http_request.query[param][0].length;
+}
+
+var newUser = function() {
+	var usr = system.new_user(prepUser.alias);
+	if(typeof usr == "number") {
+		reply.errors.push("Failed to create user record.");
+		return;
+	}
+	log("User #" + usr.number + " registered via HTTP.");
+	usr.security.password = prepUser.password;
+	for(var property in prepUser) {
+		if(property == "alias" || property == "password")
+			continue;
+		usr[property] = prepUser[property];
+	}
+	reply.userNumber = usr.number;
+}
+
+// See if the hidden form fields were filled
+if(	(	paramExists("send-me-free-stuff")
+		&&
+		http_request.query["send-me-free-stuff"][0] != ""
+	)
+	||
+	(	paramExists("subscribe-to-newsletter")
+		&&
+		http_request.query["subscribe-to-newsletter"][0] != ""
+	)
+) {
+	log("Hidden registration form input element filled.  Likely a bot.  Cancelling user registration.");
+	exit();
+}
+
+if(	system.newuser_password != ""
+	&&
+	(	typeof http_request.query["newuser-password"] == "undefined"
+		||
+		http_request.query["newuser-password"][0] != system.newuser_password
+	)
+) {
+	reply.errors.push("Incorrect registration password.");
+}
+
+// More could be done to respect certain newuser question toggles
+// (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
+
+if(!paramExists("alias") || paramLength("alias") < MIN_ALIAS || paramLength("alias") > LEN_ALIAS) {
+	reply.errors.push("Valid username is required.");
+} else if(system.matchuser(http_request.query.alias[0]) > 0) {
+	reply.errors.push("Username already taken.");
+} else {
+	prepUser.alias = cleanParam("alias");
+	prepUser.handle = cleanParam("alias");
+}
+
+if(	(!paramExists("password1") || !paramExists("password2"))
+	||
+	http_request.query.password1[0] != http_request.query.password2[0]
+) {
+	reply.errors.push("Password & confirmation are required, and must match.");
+} else if(paramLength("password1") < settings.minimum_password_length || paramLength("password1") > LEN_PASS) {
+	reply.errors.push("Password must be between " + settings.minimum_password_length + " and " + LEN_PASS + " in length.");
+} else {
+	prepUser.password = cleanParam("password1");
+}
+
+if(!paramExists("netmail") && !required(UQ_NONETMAIL))
+	reply.errors.push("Email address is required.");
+else if((paramLength("netmail") < MIN_NETMAIL || paramLength("netmail") > LEN_NETMAIL) && !required(UQ_NONETMAIL))
+	reply.errors.push("Invalid email address.");
+else
+	prepUser.netmail = cleanParam("netmail");
+
+if(required(UQ_REALNAME) && (!paramExists("realname") || paramLength("realname") < MIN_REALNAME || paramLength("realname") > LEN_NAME))
+	reply.errors.push("Valid real name is required.");
+else
+	prepUser.realname = cleanParam("realname");
+
+if(required(UQ_LOCATION) && (!paramExists("location") || paramLength("location") < MIN_LOCATION || paramLength("location") > LEN_LOCATION))
+	reply.errors.push("Valid location is required.");
+else
+	prepUser.location = cleanParam("location");
+
+if(required(UQ_ADDRESS) && (!paramExists("address") || paramLength("address") < MIN_ADDRESS || paramLength("address") > LEN_ADDRESS))
+	reply.errors.push("Valid street address is required.");
+else
+	prepUser.address = cleanParam("address");
+
+if(required(UQ_PHONE) && (!paramExists("phone") || paramLength("phone") < MIN_PHONE || paramLength("phone") > LEN_PHONE))
+	reply.errors.push("Valid phone number is required.");
+else
+	prepUser.phone = cleanParam("phone");
+
+if(required(UQ_SEX) && (!paramExists("gender") || paramLength("gender") != 1))
+	reply.errors.push("Sex is required. Heh heh.");
+else
+	prepUser.gender = cleanParam("gender");
+
+if(	paramExists("birth")
+	&&
+	http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
+) {
+	// Should really test for valid date (and date format per system config)
+	prepUser.birthdate = cleanParam("birth");
+} else if(required(UQ_BIRTH)) {
+	reply.errors.push("Birthdate is required.");
+}
+
+if(reply.errors.length < 1)
+	newUser();
+
+reply = JSON.stringify(reply);
+http_reply.header["Content-Type"] = "application/json";
+http_reply.header["Content-Length"] = reply.length;
+write(reply);
\ No newline at end of file
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
new file mode 100644
index 0000000000..e40d9f0849
--- /dev/null
+++ b/web/root/api/system.ssjs
@@ -0,0 +1,101 @@
+load("sbbsdefs.js");
+load("nodedefs.js");
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "auth.js");
+
+var reply = {};
+
+if(	(http_request.method == "GET" || http_request.method == "POST")
+	&&
+	typeof http_request.query.call != "undefined"
+	&&
+	user.number > 0
+) {
+
+	switch(http_request.query.call[0]) {
+
+		case "node-list":
+			reply = system.node_list.map(
+				function(node) {
+					if(node.status == 3)
+						var usr = new User(node.useron);
+					return ({
+						'status' : NodeStatus[node.status],
+						'action' : NodeAction[node.action],
+						'user' : (typeof usr == "undefined" ? "" : usr.alias)
+					});
+				}
+			);
+			var usr = new User(1);
+			for(var un = 1; un < system.lastuser; un++) {
+				usr.number = un;
+				if(usr.connection != "HTTP")
+					continue;
+				if(usr.alias == settings.guest)
+					continue;
+				if(usr.settings&USER_QUIET)
+					continue;
+				if(usr.logontime < time() - settings.inactivity)
+					continue;
+				var webAction = getSessionValue(usr.number, "action");
+				if(webAction === null)
+					continue;
+				reply.push(
+					{	'status' : "",
+						'action' : "viewing " + webAction,
+						'user' : usr.alias
+					}
+				);
+			}
+			break;
+
+		case "send-telegram":
+			if(user.alias == settings.guest)
+				break;
+			if(typeof http_request.query.user == "undefined")
+				break;
+			if(typeof http_request.query.telegram == "undefined" || http_request.query.telegram[0] == "")
+				break;
+			if(http_request.query.telegram[0].length > settings.maximum_telegram_length)
+				break;
+			var un = system.matchuser(http_request.query.user[0]);
+			if(un < 1)
+				break;
+			system.put_telegram(
+				un,
+				"Telegram from " +
+				user.alias + " via WWW on " + system.timestr() + "\r\n" +
+				http_request.query.telegram[0] +
+				"\r\n"
+			);
+			break;
+
+		case "get-telegram":
+			if(user.alias == settings.guest)
+				break;
+			reply.telegram = system.get_telegram(user.number);
+			break;
+
+		case "set-xtrn-intent":
+			if(user.alias == settings.guest)
+				break;
+			if(typeof http_request.query.code == "undefined")
+				break;
+			if(http_request.query.code[0].length > 8)
+				break;
+			if(typeof xtrn_area.prog[http_request.query.code[0]] == "undefined")
+				break;
+			setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
+			break;
+
+		default:
+			break;
+
+	}
+
+}
+
+reply = JSON.stringify(reply);
+http_reply.header["Content-Type"] = "application/json";
+http_reply.header["Content-Length"] = reply.length;
+write(reply);
\ No newline at end of file
diff --git a/web/root/bootstrap/css/bootstrap-theme.css b/web/root/bootstrap/css/bootstrap-theme.css
new file mode 100644
index 0000000000..c19cd5c4ba
--- /dev/null
+++ b/web/root/bootstrap/css/bootstrap-theme.css
@@ -0,0 +1,587 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+.btn-default,
+.btn-primary,
+.btn-success,
+.btn-info,
+.btn-warning,
+.btn-danger {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+}
+.btn-default:active,
+.btn-primary:active,
+.btn-success:active,
+.btn-info:active,
+.btn-warning:active,
+.btn-danger:active,
+.btn-default.active,
+.btn-primary.active,
+.btn-success.active,
+.btn-info.active,
+.btn-warning.active,
+.btn-danger.active {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-default.disabled,
+.btn-primary.disabled,
+.btn-success.disabled,
+.btn-info.disabled,
+.btn-warning.disabled,
+.btn-danger.disabled,
+.btn-default[disabled],
+.btn-primary[disabled],
+.btn-success[disabled],
+.btn-info[disabled],
+.btn-warning[disabled],
+.btn-danger[disabled],
+fieldset[disabled] .btn-default,
+fieldset[disabled] .btn-primary,
+fieldset[disabled] .btn-success,
+fieldset[disabled] .btn-info,
+fieldset[disabled] .btn-warning,
+fieldset[disabled] .btn-danger {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-default .badge,
+.btn-primary .badge,
+.btn-success .badge,
+.btn-info .badge,
+.btn-warning .badge,
+.btn-danger .badge {
+  text-shadow: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+}
+.btn-default {
+  text-shadow: 0 1px 0 #fff;
+  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image:      -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
+  background-image:         linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #dbdbdb;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus {
+  background-color: #e0e0e0;
+  background-position: 0 -15px;
+}
+.btn-default:active,
+.btn-default.active {
+  background-color: #e0e0e0;
+  border-color: #dbdbdb;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+  background-color: #e0e0e0;
+  background-image: none;
+}
+.btn-primary {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #245580;
+}
+.btn-primary:hover,
+.btn-primary:focus {
+  background-color: #265a88;
+  background-position: 0 -15px;
+}
+.btn-primary:active,
+.btn-primary.active {
+  background-color: #265a88;
+  border-color: #245580;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #265a88;
+  background-image: none;
+}
+.btn-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image:      -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #3e8f3e;
+}
+.btn-success:hover,
+.btn-success:focus {
+  background-color: #419641;
+  background-position: 0 -15px;
+}
+.btn-success:active,
+.btn-success.active {
+  background-color: #419641;
+  border-color: #3e8f3e;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #419641;
+  background-image: none;
+}
+.btn-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image:      -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #28a4c9;
+}
+.btn-info:hover,
+.btn-info:focus {
+  background-color: #2aabd2;
+  background-position: 0 -15px;
+}
+.btn-info:active,
+.btn-info.active {
+  background-color: #2aabd2;
+  border-color: #28a4c9;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #2aabd2;
+  background-image: none;
+}
+.btn-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #e38d13;
+}
+.btn-warning:hover,
+.btn-warning:focus {
+  background-color: #eb9316;
+  background-position: 0 -15px;
+}
+.btn-warning:active,
+.btn-warning.active {
+  background-color: #eb9316;
+  border-color: #e38d13;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #eb9316;
+  background-image: none;
+}
+.btn-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image:      -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #b92c28;
+}
+.btn-danger:hover,
+.btn-danger:focus {
+  background-color: #c12e2a;
+  background-position: 0 -15px;
+}
+.btn-danger:active,
+.btn-danger.active {
+  background-color: #c12e2a;
+  border-color: #b92c28;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #c12e2a;
+  background-image: none;
+}
+.thumbnail,
+.img-thumbnail {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  background-color: #e8e8e8;
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  background-color: #2e6da4;
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+  background-repeat: repeat-x;
+}
+.navbar-default {
+  background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image:      -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
+  background-image:         linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+  background-image:      -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
+  background-image:         linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+}
+.navbar-brand,
+.navbar-nav > li > a {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
+}
+.navbar-inverse {
+  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image:      -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
+  background-image:         linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+  background-image:      -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
+  background-image:         linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+}
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
+}
+.navbar-static-top,
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  border-radius: 0;
+}
+@media (max-width: 767px) {
+  .navbar .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+    background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+    background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+    background-repeat: repeat-x;
+  }
+}
+.alert {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+}
+.alert-success {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image:      -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #b2dba1;
+}
+.alert-info {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image:      -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #9acfea;
+}
+.alert-warning {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #f5e79e;
+}
+.alert-danger {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image:      -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dca7a7;
+}
+.progress {
+  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image:      -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
+  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #286090 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #286090 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image:      -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image:      -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image:      -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.list-group {
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  text-shadow: 0 -1px 0 #286090;
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #2b669a;
+}
+.list-group-item.active .badge,
+.list-group-item.active:hover .badge,
+.list-group-item.active:focus .badge {
+  text-shadow: none;
+}
+.panel {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+}
+.panel-default > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-primary > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-success > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image:      -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-info > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image:      -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-warning > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-danger > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image:      -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.well {
+  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image:      -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
+  background-image:         linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dcdcdc;
+  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+}
+/*# sourceMappingURL=bootstrap-theme.css.map */
diff --git a/web/root/bootstrap/css/bootstrap-theme.css.map b/web/root/bootstrap/css/bootstrap-theme.css.map
new file mode 100644
index 0000000000..753531147d
--- /dev/null
+++ b/web/root/bootstrap/css/bootstrap-theme.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"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","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n  text-shadow: none;\n}\n.btn:active,\n.btn.active {\n  background-image: none;\n}\n.btn-default {\n  background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n  background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #dbdbdb;\n  text-shadow: 0 1px 0 #fff;\n  border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n  background-color: #e0e0e0;\n  background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n  background-color: #e0e0e0;\n  border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #e0e0e0;\n  background-image: none;\n}\n.btn-primary {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n  background-color: #265a88;\n  background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n  background-color: #265a88;\n  border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #265a88;\n  background-image: none;\n}\n.btn-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n  background-color: #419641;\n  background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n  background-color: #419641;\n  border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #419641;\n  background-image: none;\n}\n.btn-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n  background-color: #2aabd2;\n  background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n  background-color: #2aabd2;\n  border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #2aabd2;\n  background-image: none;\n}\n.btn-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n  background-color: #eb9316;\n  background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n  background-color: #eb9316;\n  border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #eb9316;\n  background-image: none;\n}\n.btn-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n  background-color: #c12e2a;\n  background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n  background-color: #c12e2a;\n  border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #c12e2a;\n  background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n  background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n  background-color: #2e6da4;\n}\n.navbar-default {\n  background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n  background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n  background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n  background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n  background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n  background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n  background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n  background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n  box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n@media (max-width: 767px) {\n  .navbar .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #fff;\n    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n    background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n    background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n    background-repeat: repeat-x;\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n  }\n}\n.alert {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n  border-color: #b2dba1;\n}\n.alert-info {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n  border-color: #9acfea;\n}\n.alert-warning {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n  border-color: #f5e79e;\n}\n.alert-danger {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n  border-color: #dca7a7;\n}\n.progress {\n  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 #286090;\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n  border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n  text-shadow: none;\n}\n.panel {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n  border-color: #dcdcdc;\n  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  // Reset the shadow\n  &:active,\n  &.active {\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    .box-shadow(none);\n  }\n\n  .badge {\n    text-shadow: none;\n  }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n  #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n  .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n  background-repeat: repeat-x;\n  border-color: darken(@btn-color, 14%);\n\n  &:hover,\n  &:focus  {\n    background-color: darken(@btn-color, 12%);\n    background-position: 0 -15px;\n  }\n\n  &:active,\n  &.active {\n    background-color: darken(@btn-color, 12%);\n    border-color: darken(@btn-color, 14%);\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &.focus,\n    &:active,\n    &.active {\n      background-color: darken(@btn-color, 12%);\n      background-image: none;\n    }\n  }\n}\n\n// Common styles\n.btn {\n  // Remove the gradient for the pressed/active state\n  &:active,\n  &.active {\n    background-image: none;\n  }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info    { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger  { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n  background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n  background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n  border-radius: @navbar-border-radius;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  .navbar-nav > .open > a,\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n  }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n  #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n  border-radius: @navbar-border-radius;\n  .navbar-nav > .open > a,\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n  }\n\n  .navbar-brand,\n  .navbar-nav > li > a {\n    text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n  }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n  .navbar .navbar-nav .open .dropdown-menu > .active > a {\n    &,\n    &:hover,\n    &:focus {\n      color: #fff;\n      #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n    }\n  }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n  text-shadow: 0 1px 0 rgba(255,255,255,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n  .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n  border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success    { .alert-styles(@alert-success-bg); }\n.alert-info       { .alert-styles(@alert-info-bg); }\n.alert-warning    { .alert-styles(@alert-warning-bg); }\n.alert-danger     { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n  #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar            { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success    { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info       { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning    { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger     { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n  #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n  border-radius: @border-radius-base;\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n  #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n  border-color: darken(@list-group-active-border, 7.5%);\n\n  .badge {\n    text-shadow: none;\n  }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n  .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading   { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading   { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading   { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading      { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading   { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading    { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n  #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n  border-color: darken(@well-bg, 10%);\n  @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n       -o-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n  -webkit-animation-fill-mode: @fill-mode;\n          animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  // Firefox\n  &::-moz-placeholder {\n    color: @color;\n    opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n  }\n  &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n  -webkit-transform: scale(@ratio);\n      -ms-transform: scale(@ratio); // IE9 only\n       -o-transform: scale(@ratio);\n          transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n  -webkit-transform: scale(@ratioX, @ratioY);\n      -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n       -o-transform: scale(@ratioX, @ratioY);\n          transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n  -webkit-transform: scaleX(@ratio);\n      -ms-transform: scaleX(@ratio); // IE9 only\n       -o-transform: scaleX(@ratio);\n          transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n  -webkit-transform: scaleY(@ratio);\n      -ms-transform: scaleY(@ratio); // IE9 only\n       -o-transform: scaleY(@ratio);\n          transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n  -webkit-transform: skewX(@x) skewY(@y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n       -o-transform: skewX(@x) skewY(@y);\n          transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n       -o-transform: translate(@x, @y);\n          transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n       -o-transform: rotate(@degrees);\n          transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n       -o-transform: rotateX(@degrees);\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n       -o-transform: rotateY(@degrees);\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n  -webkit-transition: @transition;\n       -o-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n  -webkit-transition-timing-function: @timing-function;\n          transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n    background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Opera 12\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}
\ No newline at end of file
diff --git a/web/root/bootstrap/css/bootstrap-theme.min.css b/web/root/bootstrap/css/bootstrap-theme.min.css
new file mode 100644
index 0000000000..61358b13d0
--- /dev/null
+++ b/web/root/bootstrap/css/bootstrap-theme.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file
diff --git a/web/root/bootstrap/css/bootstrap.css b/web/root/bootstrap/css/bootstrap.css
new file mode 100644
index 0000000000..680e768786
--- /dev/null
+++ b/web/root/bootstrap/css/bootstrap.css
@@ -0,0 +1,6800 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+html {
+  font-family: sans-serif;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+body {
+  margin: 0;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+  display: block;
+}
+audio,
+canvas,
+progress,
+video {
+  display: inline-block;
+  vertical-align: baseline;
+}
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+[hidden],
+template {
+  display: none;
+}
+a {
+  background-color: transparent;
+}
+a:active,
+a:hover {
+  outline: 0;
+}
+abbr[title] {
+  border-bottom: 1px dotted;
+}
+b,
+strong {
+  font-weight: bold;
+}
+dfn {
+  font-style: italic;
+}
+h1 {
+  margin: .67em 0;
+  font-size: 2em;
+}
+mark {
+  color: #000;
+  background: #ff0;
+}
+small {
+  font-size: 80%;
+}
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+sup {
+  top: -.5em;
+}
+sub {
+  bottom: -.25em;
+}
+img {
+  border: 0;
+}
+svg:not(:root) {
+  overflow: hidden;
+}
+figure {
+  margin: 1em 40px;
+}
+hr {
+  height: 0;
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+}
+pre {
+  overflow: auto;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+  margin: 0;
+  font: inherit;
+  color: inherit;
+}
+button {
+  overflow: visible;
+}
+button,
+select {
+  text-transform: none;
+}
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  -webkit-appearance: button;
+  cursor: pointer;
+}
+button[disabled],
+html input[disabled] {
+  cursor: default;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+input {
+  line-height: normal;
+}
+input[type="checkbox"],
+input[type="radio"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+  padding: 0;
+}
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+fieldset {
+  padding: .35em .625em .75em;
+  margin: 0 2px;
+  border: 1px solid #c0c0c0;
+}
+legend {
+  padding: 0;
+  border: 0;
+}
+textarea {
+  overflow: auto;
+}
+optgroup {
+  font-weight: bold;
+}
+table {
+  border-spacing: 0;
+  border-collapse: collapse;
+}
+td,
+th {
+  padding: 0;
+}
+/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
+@media print {
+  *,
+  *:before,
+  *:after {
+    color: #000 !important;
+    text-shadow: none !important;
+    background: transparent !important;
+    -webkit-box-shadow: none !important;
+            box-shadow: none !important;
+  }
+  a,
+  a:visited {
+    text-decoration: underline;
+  }
+  a[href]:after {
+    content: " (" attr(href) ")";
+  }
+  abbr[title]:after {
+    content: " (" attr(title) ")";
+  }
+  a[href^="#"]:after,
+  a[href^="javascript:"]:after {
+    content: "";
+  }
+  pre,
+  blockquote {
+    border: 1px solid #999;
+
+    page-break-inside: avoid;
+  }
+  thead {
+    display: table-header-group;
+  }
+  tr,
+  img {
+    page-break-inside: avoid;
+  }
+  img {
+    max-width: 100% !important;
+  }
+  p,
+  h2,
+  h3 {
+    orphans: 3;
+    widows: 3;
+  }
+  h2,
+  h3 {
+    page-break-after: avoid;
+  }
+  .navbar {
+    display: none;
+  }
+  .btn > .caret,
+  .dropup > .btn > .caret {
+    border-top-color: #000 !important;
+  }
+  .label {
+    border: 1px solid #000;
+  }
+  .table {
+    border-collapse: collapse !important;
+  }
+  .table td,
+  .table th {
+    background-color: #fff !important;
+  }
+  .table-bordered th,
+  .table-bordered td {
+    border: 1px solid #ddd !important;
+  }
+}
+@font-face {
+  font-family: 'Glyphicons Halflings';
+
+  src: url('../fonts/glyphicons-halflings-regular.eot');
+  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+.glyphicon {
+  position: relative;
+  top: 1px;
+  display: inline-block;
+  font-family: 'Glyphicons Halflings';
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1;
+
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.glyphicon-asterisk:before {
+  content: "\2a";
+}
+.glyphicon-plus:before {
+  content: "\2b";
+}
+.glyphicon-euro:before,
+.glyphicon-eur:before {
+  content: "\20ac";
+}
+.glyphicon-minus:before {
+  content: "\2212";
+}
+.glyphicon-cloud:before {
+  content: "\2601";
+}
+.glyphicon-envelope:before {
+  content: "\2709";
+}
+.glyphicon-pencil:before {
+  content: "\270f";
+}
+.glyphicon-glass:before {
+  content: "\e001";
+}
+.glyphicon-music:before {
+  content: "\e002";
+}
+.glyphicon-search:before {
+  content: "\e003";
+}
+.glyphicon-heart:before {
+  content: "\e005";
+}
+.glyphicon-star:before {
+  content: "\e006";
+}
+.glyphicon-star-empty:before {
+  content: "\e007";
+}
+.glyphicon-user:before {
+  content: "\e008";
+}
+.glyphicon-film:before {
+  content: "\e009";
+}
+.glyphicon-th-large:before {
+  content: "\e010";
+}
+.glyphicon-th:before {
+  content: "\e011";
+}
+.glyphicon-th-list:before {
+  content: "\e012";
+}
+.glyphicon-ok:before {
+  content: "\e013";
+}
+.glyphicon-remove:before {
+  content: "\e014";
+}
+.glyphicon-zoom-in:before {
+  content: "\e015";
+}
+.glyphicon-zoom-out:before {
+  content: "\e016";
+}
+.glyphicon-off:before {
+  content: "\e017";
+}
+.glyphicon-signal:before {
+  content: "\e018";
+}
+.glyphicon-cog:before {
+  content: "\e019";
+}
+.glyphicon-trash:before {
+  content: "\e020";
+}
+.glyphicon-home:before {
+  content: "\e021";
+}
+.glyphicon-file:before {
+  content: "\e022";
+}
+.glyphicon-time:before {
+  content: "\e023";
+}
+.glyphicon-road:before {
+  content: "\e024";
+}
+.glyphicon-download-alt:before {
+  content: "\e025";
+}
+.glyphicon-download:before {
+  content: "\e026";
+}
+.glyphicon-upload:before {
+  content: "\e027";
+}
+.glyphicon-inbox:before {
+  content: "\e028";
+}
+.glyphicon-play-circle:before {
+  content: "\e029";
+}
+.glyphicon-repeat:before {
+  content: "\e030";
+}
+.glyphicon-refresh:before {
+  content: "\e031";
+}
+.glyphicon-list-alt:before {
+  content: "\e032";
+}
+.glyphicon-lock:before {
+  content: "\e033";
+}
+.glyphicon-flag:before {
+  content: "\e034";
+}
+.glyphicon-headphones:before {
+  content: "\e035";
+}
+.glyphicon-volume-off:before {
+  content: "\e036";
+}
+.glyphicon-volume-down:before {
+  content: "\e037";
+}
+.glyphicon-volume-up:before {
+  content: "\e038";
+}
+.glyphicon-qrcode:before {
+  content: "\e039";
+}
+.glyphicon-barcode:before {
+  content: "\e040";
+}
+.glyphicon-tag:before {
+  content: "\e041";
+}
+.glyphicon-tags:before {
+  content: "\e042";
+}
+.glyphicon-book:before {
+  content: "\e043";
+}
+.glyphicon-bookmark:before {
+  content: "\e044";
+}
+.glyphicon-print:before {
+  content: "\e045";
+}
+.glyphicon-camera:before {
+  content: "\e046";
+}
+.glyphicon-font:before {
+  content: "\e047";
+}
+.glyphicon-bold:before {
+  content: "\e048";
+}
+.glyphicon-italic:before {
+  content: "\e049";
+}
+.glyphicon-text-height:before {
+  content: "\e050";
+}
+.glyphicon-text-width:before {
+  content: "\e051";
+}
+.glyphicon-align-left:before {
+  content: "\e052";
+}
+.glyphicon-align-center:before {
+  content: "\e053";
+}
+.glyphicon-align-right:before {
+  content: "\e054";
+}
+.glyphicon-align-justify:before {
+  content: "\e055";
+}
+.glyphicon-list:before {
+  content: "\e056";
+}
+.glyphicon-indent-left:before {
+  content: "\e057";
+}
+.glyphicon-indent-right:before {
+  content: "\e058";
+}
+.glyphicon-facetime-video:before {
+  content: "\e059";
+}
+.glyphicon-picture:before {
+  content: "\e060";
+}
+.glyphicon-map-marker:before {
+  content: "\e062";
+}
+.glyphicon-adjust:before {
+  content: "\e063";
+}
+.glyphicon-tint:before {
+  content: "\e064";
+}
+.glyphicon-edit:before {
+  content: "\e065";
+}
+.glyphicon-share:before {
+  content: "\e066";
+}
+.glyphicon-check:before {
+  content: "\e067";
+}
+.glyphicon-move:before {
+  content: "\e068";
+}
+.glyphicon-step-backward:before {
+  content: "\e069";
+}
+.glyphicon-fast-backward:before {
+  content: "\e070";
+}
+.glyphicon-backward:before {
+  content: "\e071";
+}
+.glyphicon-play:before {
+  content: "\e072";
+}
+.glyphicon-pause:before {
+  content: "\e073";
+}
+.glyphicon-stop:before {
+  content: "\e074";
+}
+.glyphicon-forward:before {
+  content: "\e075";
+}
+.glyphicon-fast-forward:before {
+  content: "\e076";
+}
+.glyphicon-step-forward:before {
+  content: "\e077";
+}
+.glyphicon-eject:before {
+  content: "\e078";
+}
+.glyphicon-chevron-left:before {
+  content: "\e079";
+}
+.glyphicon-chevron-right:before {
+  content: "\e080";
+}
+.glyphicon-plus-sign:before {
+  content: "\e081";
+}
+.glyphicon-minus-sign:before {
+  content: "\e082";
+}
+.glyphicon-remove-sign:before {
+  content: "\e083";
+}
+.glyphicon-ok-sign:before {
+  content: "\e084";
+}
+.glyphicon-question-sign:before {
+  content: "\e085";
+}
+.glyphicon-info-sign:before {
+  content: "\e086";
+}
+.glyphicon-screenshot:before {
+  content: "\e087";
+}
+.glyphicon-remove-circle:before {
+  content: "\e088";
+}
+.glyphicon-ok-circle:before {
+  content: "\e089";
+}
+.glyphicon-ban-circle:before {
+  content: "\e090";
+}
+.glyphicon-arrow-left:before {
+  content: "\e091";
+}
+.glyphicon-arrow-right:before {
+  content: "\e092";
+}
+.glyphicon-arrow-up:before {
+  content: "\e093";
+}
+.glyphicon-arrow-down:before {
+  content: "\e094";
+}
+.glyphicon-share-alt:before {
+  content: "\e095";
+}
+.glyphicon-resize-full:before {
+  content: "\e096";
+}
+.glyphicon-resize-small:before {
+  content: "\e097";
+}
+.glyphicon-exclamation-sign:before {
+  content: "\e101";
+}
+.glyphicon-gift:before {
+  content: "\e102";
+}
+.glyphicon-leaf:before {
+  content: "\e103";
+}
+.glyphicon-fire:before {
+  content: "\e104";
+}
+.glyphicon-eye-open:before {
+  content: "\e105";
+}
+.glyphicon-eye-close:before {
+  content: "\e106";
+}
+.glyphicon-warning-sign:before {
+  content: "\e107";
+}
+.glyphicon-plane:before {
+  content: "\e108";
+}
+.glyphicon-calendar:before {
+  content: "\e109";
+}
+.glyphicon-random:before {
+  content: "\e110";
+}
+.glyphicon-comment:before {
+  content: "\e111";
+}
+.glyphicon-magnet:before {
+  content: "\e112";
+}
+.glyphicon-chevron-up:before {
+  content: "\e113";
+}
+.glyphicon-chevron-down:before {
+  content: "\e114";
+}
+.glyphicon-retweet:before {
+  content: "\e115";
+}
+.glyphicon-shopping-cart:before {
+  content: "\e116";
+}
+.glyphicon-folder-close:before {
+  content: "\e117";
+}
+.glyphicon-folder-open:before {
+  content: "\e118";
+}
+.glyphicon-resize-vertical:before {
+  content: "\e119";
+}
+.glyphicon-resize-horizontal:before {
+  content: "\e120";
+}
+.glyphicon-hdd:before {
+  content: "\e121";
+}
+.glyphicon-bullhorn:before {
+  content: "\e122";
+}
+.glyphicon-bell:before {
+  content: "\e123";
+}
+.glyphicon-certificate:before {
+  content: "\e124";
+}
+.glyphicon-thumbs-up:before {
+  content: "\e125";
+}
+.glyphicon-thumbs-down:before {
+  content: "\e126";
+}
+.glyphicon-hand-right:before {
+  content: "\e127";
+}
+.glyphicon-hand-left:before {
+  content: "\e128";
+}
+.glyphicon-hand-up:before {
+  content: "\e129";
+}
+.glyphicon-hand-down:before {
+  content: "\e130";
+}
+.glyphicon-circle-arrow-right:before {
+  content: "\e131";
+}
+.glyphicon-circle-arrow-left:before {
+  content: "\e132";
+}
+.glyphicon-circle-arrow-up:before {
+  content: "\e133";
+}
+.glyphicon-circle-arrow-down:before {
+  content: "\e134";
+}
+.glyphicon-globe:before {
+  content: "\e135";
+}
+.glyphicon-wrench:before {
+  content: "\e136";
+}
+.glyphicon-tasks:before {
+  content: "\e137";
+}
+.glyphicon-filter:before {
+  content: "\e138";
+}
+.glyphicon-briefcase:before {
+  content: "\e139";
+}
+.glyphicon-fullscreen:before {
+  content: "\e140";
+}
+.glyphicon-dashboard:before {
+  content: "\e141";
+}
+.glyphicon-paperclip:before {
+  content: "\e142";
+}
+.glyphicon-heart-empty:before {
+  content: "\e143";
+}
+.glyphicon-link:before {
+  content: "\e144";
+}
+.glyphicon-phone:before {
+  content: "\e145";
+}
+.glyphicon-pushpin:before {
+  content: "\e146";
+}
+.glyphicon-usd:before {
+  content: "\e148";
+}
+.glyphicon-gbp:before {
+  content: "\e149";
+}
+.glyphicon-sort:before {
+  content: "\e150";
+}
+.glyphicon-sort-by-alphabet:before {
+  content: "\e151";
+}
+.glyphicon-sort-by-alphabet-alt:before {
+  content: "\e152";
+}
+.glyphicon-sort-by-order:before {
+  content: "\e153";
+}
+.glyphicon-sort-by-order-alt:before {
+  content: "\e154";
+}
+.glyphicon-sort-by-attributes:before {
+  content: "\e155";
+}
+.glyphicon-sort-by-attributes-alt:before {
+  content: "\e156";
+}
+.glyphicon-unchecked:before {
+  content: "\e157";
+}
+.glyphicon-expand:before {
+  content: "\e158";
+}
+.glyphicon-collapse-down:before {
+  content: "\e159";
+}
+.glyphicon-collapse-up:before {
+  content: "\e160";
+}
+.glyphicon-log-in:before {
+  content: "\e161";
+}
+.glyphicon-flash:before {
+  content: "\e162";
+}
+.glyphicon-log-out:before {
+  content: "\e163";
+}
+.glyphicon-new-window:before {
+  content: "\e164";
+}
+.glyphicon-record:before {
+  content: "\e165";
+}
+.glyphicon-save:before {
+  content: "\e166";
+}
+.glyphicon-open:before {
+  content: "\e167";
+}
+.glyphicon-saved:before {
+  content: "\e168";
+}
+.glyphicon-import:before {
+  content: "\e169";
+}
+.glyphicon-export:before {
+  content: "\e170";
+}
+.glyphicon-send:before {
+  content: "\e171";
+}
+.glyphicon-floppy-disk:before {
+  content: "\e172";
+}
+.glyphicon-floppy-saved:before {
+  content: "\e173";
+}
+.glyphicon-floppy-remove:before {
+  content: "\e174";
+}
+.glyphicon-floppy-save:before {
+  content: "\e175";
+}
+.glyphicon-floppy-open:before {
+  content: "\e176";
+}
+.glyphicon-credit-card:before {
+  content: "\e177";
+}
+.glyphicon-transfer:before {
+  content: "\e178";
+}
+.glyphicon-cutlery:before {
+  content: "\e179";
+}
+.glyphicon-header:before {
+  content: "\e180";
+}
+.glyphicon-compressed:before {
+  content: "\e181";
+}
+.glyphicon-earphone:before {
+  content: "\e182";
+}
+.glyphicon-phone-alt:before {
+  content: "\e183";
+}
+.glyphicon-tower:before {
+  content: "\e184";
+}
+.glyphicon-stats:before {
+  content: "\e185";
+}
+.glyphicon-sd-video:before {
+  content: "\e186";
+}
+.glyphicon-hd-video:before {
+  content: "\e187";
+}
+.glyphicon-subtitles:before {
+  content: "\e188";
+}
+.glyphicon-sound-stereo:before {
+  content: "\e189";
+}
+.glyphicon-sound-dolby:before {
+  content: "\e190";
+}
+.glyphicon-sound-5-1:before {
+  content: "\e191";
+}
+.glyphicon-sound-6-1:before {
+  content: "\e192";
+}
+.glyphicon-sound-7-1:before {
+  content: "\e193";
+}
+.glyphicon-copyright-mark:before {
+  content: "\e194";
+}
+.glyphicon-registration-mark:before {
+  content: "\e195";
+}
+.glyphicon-cloud-download:before {
+  content: "\e197";
+}
+.glyphicon-cloud-upload:before {
+  content: "\e198";
+}
+.glyphicon-tree-conifer:before {
+  content: "\e199";
+}
+.glyphicon-tree-deciduous:before {
+  content: "\e200";
+}
+.glyphicon-cd:before {
+  content: "\e201";
+}
+.glyphicon-save-file:before {
+  content: "\e202";
+}
+.glyphicon-open-file:before {
+  content: "\e203";
+}
+.glyphicon-level-up:before {
+  content: "\e204";
+}
+.glyphicon-copy:before {
+  content: "\e205";
+}
+.glyphicon-paste:before {
+  content: "\e206";
+}
+.glyphicon-alert:before {
+  content: "\e209";
+}
+.glyphicon-equalizer:before {
+  content: "\e210";
+}
+.glyphicon-king:before {
+  content: "\e211";
+}
+.glyphicon-queen:before {
+  content: "\e212";
+}
+.glyphicon-pawn:before {
+  content: "\e213";
+}
+.glyphicon-bishop:before {
+  content: "\e214";
+}
+.glyphicon-knight:before {
+  content: "\e215";
+}
+.glyphicon-baby-formula:before {
+  content: "\e216";
+}
+.glyphicon-tent:before {
+  content: "\26fa";
+}
+.glyphicon-blackboard:before {
+  content: "\e218";
+}
+.glyphicon-bed:before {
+  content: "\e219";
+}
+.glyphicon-apple:before {
+  content: "\f8ff";
+}
+.glyphicon-erase:before {
+  content: "\e221";
+}
+.glyphicon-hourglass:before {
+  content: "\231b";
+}
+.glyphicon-lamp:before {
+  content: "\e223";
+}
+.glyphicon-duplicate:before {
+  content: "\e224";
+}
+.glyphicon-piggy-bank:before {
+  content: "\e225";
+}
+.glyphicon-scissors:before {
+  content: "\e226";
+}
+.glyphicon-bitcoin:before {
+  content: "\e227";
+}
+.glyphicon-btc:before {
+  content: "\e227";
+}
+.glyphicon-xbt:before {
+  content: "\e227";
+}
+.glyphicon-yen:before {
+  content: "\00a5";
+}
+.glyphicon-jpy:before {
+  content: "\00a5";
+}
+.glyphicon-ruble:before {
+  content: "\20bd";
+}
+.glyphicon-rub:before {
+  content: "\20bd";
+}
+.glyphicon-scale:before {
+  content: "\e230";
+}
+.glyphicon-ice-lolly:before {
+  content: "\e231";
+}
+.glyphicon-ice-lolly-tasted:before {
+  content: "\e232";
+}
+.glyphicon-education:before {
+  content: "\e233";
+}
+.glyphicon-option-horizontal:before {
+  content: "\e234";
+}
+.glyphicon-option-vertical:before {
+  content: "\e235";
+}
+.glyphicon-menu-hamburger:before {
+  content: "\e236";
+}
+.glyphicon-modal-window:before {
+  content: "\e237";
+}
+.glyphicon-oil:before {
+  content: "\e238";
+}
+.glyphicon-grain:before {
+  content: "\e239";
+}
+.glyphicon-sunglasses:before {
+  content: "\e240";
+}
+.glyphicon-text-size:before {
+  content: "\e241";
+}
+.glyphicon-text-color:before {
+  content: "\e242";
+}
+.glyphicon-text-background:before {
+  content: "\e243";
+}
+.glyphicon-object-align-top:before {
+  content: "\e244";
+}
+.glyphicon-object-align-bottom:before {
+  content: "\e245";
+}
+.glyphicon-object-align-horizontal:before {
+  content: "\e246";
+}
+.glyphicon-object-align-left:before {
+  content: "\e247";
+}
+.glyphicon-object-align-vertical:before {
+  content: "\e248";
+}
+.glyphicon-object-align-right:before {
+  content: "\e249";
+}
+.glyphicon-triangle-right:before {
+  content: "\e250";
+}
+.glyphicon-triangle-left:before {
+  content: "\e251";
+}
+.glyphicon-triangle-bottom:before {
+  content: "\e252";
+}
+.glyphicon-triangle-top:before {
+  content: "\e253";
+}
+.glyphicon-console:before {
+  content: "\e254";
+}
+.glyphicon-superscript:before {
+  content: "\e255";
+}
+.glyphicon-subscript:before {
+  content: "\e256";
+}
+.glyphicon-menu-left:before {
+  content: "\e257";
+}
+.glyphicon-menu-right:before {
+  content: "\e258";
+}
+.glyphicon-menu-down:before {
+  content: "\e259";
+}
+.glyphicon-menu-up:before {
+  content: "\e260";
+}
+* {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+*:before,
+*:after {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+html {
+  font-size: 10px;
+
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #333;
+  background-color: #fff;
+}
+input,
+button,
+select,
+textarea {
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+a {
+  color: #337ab7;
+  text-decoration: none;
+}
+a:hover,
+a:focus {
+  color: #23527c;
+  text-decoration: underline;
+}
+a:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+figure {
+  margin: 0;
+}
+img {
+  vertical-align: middle;
+}
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  display: block;
+  max-width: 100%;
+  height: auto;
+}
+.img-rounded {
+  border-radius: 6px;
+}
+.img-thumbnail {
+  display: inline-block;
+  max-width: 100%;
+  height: auto;
+  padding: 4px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: all .2s ease-in-out;
+       -o-transition: all .2s ease-in-out;
+          transition: all .2s ease-in-out;
+}
+.img-circle {
+  border-radius: 50%;
+}
+hr {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  border: 0;
+  border-top: 1px solid #eee;
+}
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+  position: static;
+  width: auto;
+  height: auto;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+}
+[role="button"] {
+  cursor: pointer;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
+  font-weight: normal;
+  line-height: 1;
+  color: #777;
+}
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.h3 {
+  margin-top: 20px;
+  margin-bottom: 10px;
+}
+h1 small,
+.h1 small,
+h2 small,
+.h2 small,
+h3 small,
+.h3 small,
+h1 .small,
+.h1 .small,
+h2 .small,
+.h2 .small,
+h3 .small,
+.h3 .small {
+  font-size: 65%;
+}
+h4,
+.h4,
+h5,
+.h5,
+h6,
+.h6 {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+h4 small,
+.h4 small,
+h5 small,
+.h5 small,
+h6 small,
+.h6 small,
+h4 .small,
+.h4 .small,
+h5 .small,
+.h5 .small,
+h6 .small,
+.h6 .small {
+  font-size: 75%;
+}
+h1,
+.h1 {
+  font-size: 36px;
+}
+h2,
+.h2 {
+  font-size: 30px;
+}
+h3,
+.h3 {
+  font-size: 24px;
+}
+h4,
+.h4 {
+  font-size: 18px;
+}
+h5,
+.h5 {
+  font-size: 14px;
+}
+h6,
+.h6 {
+  font-size: 12px;
+}
+p {
+  margin: 0 0 10px;
+}
+.lead {
+  margin-bottom: 20px;
+  font-size: 16px;
+  font-weight: 300;
+  line-height: 1.4;
+}
+@media (min-width: 768px) {
+  .lead {
+    font-size: 21px;
+  }
+}
+small,
+.small {
+  font-size: 85%;
+}
+mark,
+.mark {
+  padding: .2em;
+  background-color: #fcf8e3;
+}
+.text-left {
+  text-align: left;
+}
+.text-right {
+  text-align: right;
+}
+.text-center {
+  text-align: center;
+}
+.text-justify {
+  text-align: justify;
+}
+.text-nowrap {
+  white-space: nowrap;
+}
+.text-lowercase {
+  text-transform: lowercase;
+}
+.text-uppercase {
+  text-transform: uppercase;
+}
+.text-capitalize {
+  text-transform: capitalize;
+}
+.text-muted {
+  color: #777;
+}
+.text-primary {
+  color: #337ab7;
+}
+a.text-primary:hover,
+a.text-primary:focus {
+  color: #286090;
+}
+.text-success {
+  color: #3c763d;
+}
+a.text-success:hover,
+a.text-success:focus {
+  color: #2b542c;
+}
+.text-info {
+  color: #31708f;
+}
+a.text-info:hover,
+a.text-info:focus {
+  color: #245269;
+}
+.text-warning {
+  color: #8a6d3b;
+}
+a.text-warning:hover,
+a.text-warning:focus {
+  color: #66512c;
+}
+.text-danger {
+  color: #a94442;
+}
+a.text-danger:hover,
+a.text-danger:focus {
+  color: #843534;
+}
+.bg-primary {
+  color: #fff;
+  background-color: #337ab7;
+}
+a.bg-primary:hover,
+a.bg-primary:focus {
+  background-color: #286090;
+}
+.bg-success {
+  background-color: #dff0d8;
+}
+a.bg-success:hover,
+a.bg-success:focus {
+  background-color: #c1e2b3;
+}
+.bg-info {
+  background-color: #d9edf7;
+}
+a.bg-info:hover,
+a.bg-info:focus {
+  background-color: #afd9ee;
+}
+.bg-warning {
+  background-color: #fcf8e3;
+}
+a.bg-warning:hover,
+a.bg-warning:focus {
+  background-color: #f7ecb5;
+}
+.bg-danger {
+  background-color: #f2dede;
+}
+a.bg-danger:hover,
+a.bg-danger:focus {
+  background-color: #e4b9b9;
+}
+.page-header {
+  padding-bottom: 9px;
+  margin: 40px 0 20px;
+  border-bottom: 1px solid #eee;
+}
+ul,
+ol {
+  margin-top: 0;
+  margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+  margin-bottom: 0;
+}
+.list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
+.list-inline {
+  padding-left: 0;
+  margin-left: -5px;
+  list-style: none;
+}
+.list-inline > li {
+  display: inline-block;
+  padding-right: 5px;
+  padding-left: 5px;
+}
+dl {
+  margin-top: 0;
+  margin-bottom: 20px;
+}
+dt,
+dd {
+  line-height: 1.42857143;
+}
+dt {
+  font-weight: bold;
+}
+dd {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .dl-horizontal dt {
+    float: left;
+    width: 160px;
+    overflow: hidden;
+    clear: left;
+    text-align: right;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .dl-horizontal dd {
+    margin-left: 180px;
+  }
+}
+abbr[title],
+abbr[data-original-title] {
+  cursor: help;
+  border-bottom: 1px dotted #777;
+}
+.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+blockquote {
+  padding: 10px 20px;
+  margin: 0 0 20px;
+  font-size: 17.5px;
+  border-left: 5px solid #eee;
+}
+blockquote p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+  margin-bottom: 0;
+}
+blockquote footer,
+blockquote small,
+blockquote .small {
+  display: block;
+  font-size: 80%;
+  line-height: 1.42857143;
+  color: #777;
+}
+blockquote footer:before,
+blockquote small:before,
+blockquote .small:before {
+  content: '\2014 \00A0';
+}
+.blockquote-reverse,
+blockquote.pull-right {
+  padding-right: 15px;
+  padding-left: 0;
+  text-align: right;
+  border-right: 5px solid #eee;
+  border-left: 0;
+}
+.blockquote-reverse footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+  content: '';
+}
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small:after,
+blockquote.pull-right .small:after {
+  content: '\00A0 \2014';
+}
+address {
+  margin-bottom: 20px;
+  font-style: normal;
+  line-height: 1.42857143;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+code {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #c7254e;
+  background-color: #f9f2f4;
+  border-radius: 4px;
+}
+kbd {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #fff;
+  background-color: #333;
+  border-radius: 3px;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+}
+kbd kbd {
+  padding: 0;
+  font-size: 100%;
+  font-weight: bold;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+pre {
+  display: block;
+  padding: 9.5px;
+  margin: 0 0 10px;
+  font-size: 13px;
+  line-height: 1.42857143;
+  color: #333;
+  word-break: break-all;
+  word-wrap: break-word;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+pre code {
+  padding: 0;
+  font-size: inherit;
+  color: inherit;
+  white-space: pre-wrap;
+  background-color: transparent;
+  border-radius: 0;
+}
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}
+.container {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+@media (min-width: 768px) {
+  .container {
+    width: 750px;
+  }
+}
+@media (min-width: 992px) {
+  .container {
+    width: 970px;
+  }
+}
+@media (min-width: 1200px) {
+  .container {
+    width: 1170px;
+  }
+}
+.container-fluid {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+.row {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
+  position: relative;
+  min-height: 1px;
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+  float: left;
+}
+.col-xs-12 {
+  width: 100%;
+}
+.col-xs-11 {
+  width: 91.66666667%;
+}
+.col-xs-10 {
+  width: 83.33333333%;
+}
+.col-xs-9 {
+  width: 75%;
+}
+.col-xs-8 {
+  width: 66.66666667%;
+}
+.col-xs-7 {
+  width: 58.33333333%;
+}
+.col-xs-6 {
+  width: 50%;
+}
+.col-xs-5 {
+  width: 41.66666667%;
+}
+.col-xs-4 {
+  width: 33.33333333%;
+}
+.col-xs-3 {
+  width: 25%;
+}
+.col-xs-2 {
+  width: 16.66666667%;
+}
+.col-xs-1 {
+  width: 8.33333333%;
+}
+.col-xs-pull-12 {
+  right: 100%;
+}
+.col-xs-pull-11 {
+  right: 91.66666667%;
+}
+.col-xs-pull-10 {
+  right: 83.33333333%;
+}
+.col-xs-pull-9 {
+  right: 75%;
+}
+.col-xs-pull-8 {
+  right: 66.66666667%;
+}
+.col-xs-pull-7 {
+  right: 58.33333333%;
+}
+.col-xs-pull-6 {
+  right: 50%;
+}
+.col-xs-pull-5 {
+  right: 41.66666667%;
+}
+.col-xs-pull-4 {
+  right: 33.33333333%;
+}
+.col-xs-pull-3 {
+  right: 25%;
+}
+.col-xs-pull-2 {
+  right: 16.66666667%;
+}
+.col-xs-pull-1 {
+  right: 8.33333333%;
+}
+.col-xs-pull-0 {
+  right: auto;
+}
+.col-xs-push-12 {
+  left: 100%;
+}
+.col-xs-push-11 {
+  left: 91.66666667%;
+}
+.col-xs-push-10 {
+  left: 83.33333333%;
+}
+.col-xs-push-9 {
+  left: 75%;
+}
+.col-xs-push-8 {
+  left: 66.66666667%;
+}
+.col-xs-push-7 {
+  left: 58.33333333%;
+}
+.col-xs-push-6 {
+  left: 50%;
+}
+.col-xs-push-5 {
+  left: 41.66666667%;
+}
+.col-xs-push-4 {
+  left: 33.33333333%;
+}
+.col-xs-push-3 {
+  left: 25%;
+}
+.col-xs-push-2 {
+  left: 16.66666667%;
+}
+.col-xs-push-1 {
+  left: 8.33333333%;
+}
+.col-xs-push-0 {
+  left: auto;
+}
+.col-xs-offset-12 {
+  margin-left: 100%;
+}
+.col-xs-offset-11 {
+  margin-left: 91.66666667%;
+}
+.col-xs-offset-10 {
+  margin-left: 83.33333333%;
+}
+.col-xs-offset-9 {
+  margin-left: 75%;
+}
+.col-xs-offset-8 {
+  margin-left: 66.66666667%;
+}
+.col-xs-offset-7 {
+  margin-left: 58.33333333%;
+}
+.col-xs-offset-6 {
+  margin-left: 50%;
+}
+.col-xs-offset-5 {
+  margin-left: 41.66666667%;
+}
+.col-xs-offset-4 {
+  margin-left: 33.33333333%;
+}
+.col-xs-offset-3 {
+  margin-left: 25%;
+}
+.col-xs-offset-2 {
+  margin-left: 16.66666667%;
+}
+.col-xs-offset-1 {
+  margin-left: 8.33333333%;
+}
+.col-xs-offset-0 {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+    float: left;
+  }
+  .col-sm-12 {
+    width: 100%;
+  }
+  .col-sm-11 {
+    width: 91.66666667%;
+  }
+  .col-sm-10 {
+    width: 83.33333333%;
+  }
+  .col-sm-9 {
+    width: 75%;
+  }
+  .col-sm-8 {
+    width: 66.66666667%;
+  }
+  .col-sm-7 {
+    width: 58.33333333%;
+  }
+  .col-sm-6 {
+    width: 50%;
+  }
+  .col-sm-5 {
+    width: 41.66666667%;
+  }
+  .col-sm-4 {
+    width: 33.33333333%;
+  }
+  .col-sm-3 {
+    width: 25%;
+  }
+  .col-sm-2 {
+    width: 16.66666667%;
+  }
+  .col-sm-1 {
+    width: 8.33333333%;
+  }
+  .col-sm-pull-12 {
+    right: 100%;
+  }
+  .col-sm-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-sm-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-sm-pull-9 {
+    right: 75%;
+  }
+  .col-sm-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-sm-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-sm-pull-6 {
+    right: 50%;
+  }
+  .col-sm-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-sm-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-sm-pull-3 {
+    right: 25%;
+  }
+  .col-sm-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-sm-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-sm-pull-0 {
+    right: auto;
+  }
+  .col-sm-push-12 {
+    left: 100%;
+  }
+  .col-sm-push-11 {
+    left: 91.66666667%;
+  }
+  .col-sm-push-10 {
+    left: 83.33333333%;
+  }
+  .col-sm-push-9 {
+    left: 75%;
+  }
+  .col-sm-push-8 {
+    left: 66.66666667%;
+  }
+  .col-sm-push-7 {
+    left: 58.33333333%;
+  }
+  .col-sm-push-6 {
+    left: 50%;
+  }
+  .col-sm-push-5 {
+    left: 41.66666667%;
+  }
+  .col-sm-push-4 {
+    left: 33.33333333%;
+  }
+  .col-sm-push-3 {
+    left: 25%;
+  }
+  .col-sm-push-2 {
+    left: 16.66666667%;
+  }
+  .col-sm-push-1 {
+    left: 8.33333333%;
+  }
+  .col-sm-push-0 {
+    left: auto;
+  }
+  .col-sm-offset-12 {
+    margin-left: 100%;
+  }
+  .col-sm-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-sm-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-sm-offset-9 {
+    margin-left: 75%;
+  }
+  .col-sm-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-sm-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-sm-offset-6 {
+    margin-left: 50%;
+  }
+  .col-sm-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-sm-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-sm-offset-3 {
+    margin-left: 25%;
+  }
+  .col-sm-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-sm-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-sm-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 992px) {
+  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+    float: left;
+  }
+  .col-md-12 {
+    width: 100%;
+  }
+  .col-md-11 {
+    width: 91.66666667%;
+  }
+  .col-md-10 {
+    width: 83.33333333%;
+  }
+  .col-md-9 {
+    width: 75%;
+  }
+  .col-md-8 {
+    width: 66.66666667%;
+  }
+  .col-md-7 {
+    width: 58.33333333%;
+  }
+  .col-md-6 {
+    width: 50%;
+  }
+  .col-md-5 {
+    width: 41.66666667%;
+  }
+  .col-md-4 {
+    width: 33.33333333%;
+  }
+  .col-md-3 {
+    width: 25%;
+  }
+  .col-md-2 {
+    width: 16.66666667%;
+  }
+  .col-md-1 {
+    width: 8.33333333%;
+  }
+  .col-md-pull-12 {
+    right: 100%;
+  }
+  .col-md-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-md-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-md-pull-9 {
+    right: 75%;
+  }
+  .col-md-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-md-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-md-pull-6 {
+    right: 50%;
+  }
+  .col-md-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-md-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-md-pull-3 {
+    right: 25%;
+  }
+  .col-md-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-md-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-md-pull-0 {
+    right: auto;
+  }
+  .col-md-push-12 {
+    left: 100%;
+  }
+  .col-md-push-11 {
+    left: 91.66666667%;
+  }
+  .col-md-push-10 {
+    left: 83.33333333%;
+  }
+  .col-md-push-9 {
+    left: 75%;
+  }
+  .col-md-push-8 {
+    left: 66.66666667%;
+  }
+  .col-md-push-7 {
+    left: 58.33333333%;
+  }
+  .col-md-push-6 {
+    left: 50%;
+  }
+  .col-md-push-5 {
+    left: 41.66666667%;
+  }
+  .col-md-push-4 {
+    left: 33.33333333%;
+  }
+  .col-md-push-3 {
+    left: 25%;
+  }
+  .col-md-push-2 {
+    left: 16.66666667%;
+  }
+  .col-md-push-1 {
+    left: 8.33333333%;
+  }
+  .col-md-push-0 {
+    left: auto;
+  }
+  .col-md-offset-12 {
+    margin-left: 100%;
+  }
+  .col-md-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-md-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-md-offset-9 {
+    margin-left: 75%;
+  }
+  .col-md-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-md-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-md-offset-6 {
+    margin-left: 50%;
+  }
+  .col-md-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-md-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-md-offset-3 {
+    margin-left: 25%;
+  }
+  .col-md-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-md-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-md-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 1200px) {
+  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+    float: left;
+  }
+  .col-lg-12 {
+    width: 100%;
+  }
+  .col-lg-11 {
+    width: 91.66666667%;
+  }
+  .col-lg-10 {
+    width: 83.33333333%;
+  }
+  .col-lg-9 {
+    width: 75%;
+  }
+  .col-lg-8 {
+    width: 66.66666667%;
+  }
+  .col-lg-7 {
+    width: 58.33333333%;
+  }
+  .col-lg-6 {
+    width: 50%;
+  }
+  .col-lg-5 {
+    width: 41.66666667%;
+  }
+  .col-lg-4 {
+    width: 33.33333333%;
+  }
+  .col-lg-3 {
+    width: 25%;
+  }
+  .col-lg-2 {
+    width: 16.66666667%;
+  }
+  .col-lg-1 {
+    width: 8.33333333%;
+  }
+  .col-lg-pull-12 {
+    right: 100%;
+  }
+  .col-lg-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-lg-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-lg-pull-9 {
+    right: 75%;
+  }
+  .col-lg-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-lg-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-lg-pull-6 {
+    right: 50%;
+  }
+  .col-lg-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-lg-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-lg-pull-3 {
+    right: 25%;
+  }
+  .col-lg-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-lg-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-lg-pull-0 {
+    right: auto;
+  }
+  .col-lg-push-12 {
+    left: 100%;
+  }
+  .col-lg-push-11 {
+    left: 91.66666667%;
+  }
+  .col-lg-push-10 {
+    left: 83.33333333%;
+  }
+  .col-lg-push-9 {
+    left: 75%;
+  }
+  .col-lg-push-8 {
+    left: 66.66666667%;
+  }
+  .col-lg-push-7 {
+    left: 58.33333333%;
+  }
+  .col-lg-push-6 {
+    left: 50%;
+  }
+  .col-lg-push-5 {
+    left: 41.66666667%;
+  }
+  .col-lg-push-4 {
+    left: 33.33333333%;
+  }
+  .col-lg-push-3 {
+    left: 25%;
+  }
+  .col-lg-push-2 {
+    left: 16.66666667%;
+  }
+  .col-lg-push-1 {
+    left: 8.33333333%;
+  }
+  .col-lg-push-0 {
+    left: auto;
+  }
+  .col-lg-offset-12 {
+    margin-left: 100%;
+  }
+  .col-lg-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-lg-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-lg-offset-9 {
+    margin-left: 75%;
+  }
+  .col-lg-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-lg-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-lg-offset-6 {
+    margin-left: 50%;
+  }
+  .col-lg-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-lg-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-lg-offset-3 {
+    margin-left: 25%;
+  }
+  .col-lg-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-lg-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-lg-offset-0 {
+    margin-left: 0;
+  }
+}
+table {
+  background-color: transparent;
+}
+caption {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  color: #777;
+  text-align: left;
+}
+th {
+  text-align: left;
+}
+.table {
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: 20px;
+}
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+  padding: 8px;
+  line-height: 1.42857143;
+  vertical-align: top;
+  border-top: 1px solid #ddd;
+}
+.table > thead > tr > th {
+  vertical-align: bottom;
+  border-bottom: 2px solid #ddd;
+}
+.table > caption + thead > tr:first-child > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+  border-top: 0;
+}
+.table > tbody + tbody {
+  border-top: 2px solid #ddd;
+}
+.table .table {
+  background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+  padding: 5px;
+}
+.table-bordered {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+  border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-of-type(odd) {
+  background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover {
+  background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+  position: static;
+  display: table-column;
+  float: none;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+  position: static;
+  display: table-cell;
+  float: none;
+}
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+  background-color: #f5f5f5;
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+  background-color: #e8e8e8;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+  background-color: #dff0d8;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th {
+  background-color: #d0e9c6;
+}
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+  background-color: #d9edf7;
+}
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th {
+  background-color: #c4e3f3;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+  background-color: #fcf8e3;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th {
+  background-color: #faf2cc;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+  background-color: #f2dede;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th {
+  background-color: #ebcccc;
+}
+.table-responsive {
+  min-height: .01%;
+  overflow-x: auto;
+}
+@media screen and (max-width: 767px) {
+  .table-responsive {
+    width: 100%;
+    margin-bottom: 15px;
+    overflow-y: hidden;
+    -ms-overflow-style: -ms-autohiding-scrollbar;
+    border: 1px solid #ddd;
+  }
+  .table-responsive > .table {
+    margin-bottom: 0;
+  }
+  .table-responsive > .table > thead > tr > th,
+  .table-responsive > .table > tbody > tr > th,
+  .table-responsive > .table > tfoot > tr > th,
+  .table-responsive > .table > thead > tr > td,
+  .table-responsive > .table > tbody > tr > td,
+  .table-responsive > .table > tfoot > tr > td {
+    white-space: nowrap;
+  }
+  .table-responsive > .table-bordered {
+    border: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:first-child,
+  .table-responsive > .table-bordered > tbody > tr > th:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+  .table-responsive > .table-bordered > thead > tr > td:first-child,
+  .table-responsive > .table-bordered > tbody > tr > td:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+    border-left: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:last-child,
+  .table-responsive > .table-bordered > tbody > tr > th:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+  .table-responsive > .table-bordered > thead > tr > td:last-child,
+  .table-responsive > .table-bordered > tbody > tr > td:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+    border-right: 0;
+  }
+  .table-responsive > .table-bordered > tbody > tr:last-child > th,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+  .table-responsive > .table-bordered > tbody > tr:last-child > td,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+    border-bottom: 0;
+  }
+}
+fieldset {
+  min-width: 0;
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: 20px;
+  font-size: 21px;
+  line-height: inherit;
+  color: #333;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+}
+label {
+  display: inline-block;
+  max-width: 100%;
+  margin-bottom: 5px;
+  font-weight: bold;
+}
+input[type="search"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  margin-top: 1px \9;
+  line-height: normal;
+}
+input[type="file"] {
+  display: block;
+}
+input[type="range"] {
+  display: block;
+  width: 100%;
+}
+select[multiple],
+select[size] {
+  height: auto;
+}
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+output {
+  display: block;
+  padding-top: 7px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+}
+.form-control {
+  display: block;
+  width: 100%;
+  height: 34px;
+  padding: 6px 12px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+  -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
+       -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+          transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+.form-control:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.form-control::-moz-placeholder {
+  color: #999;
+  opacity: 1;
+}
+.form-control:-ms-input-placeholder {
+  color: #999;
+}
+.form-control::-webkit-input-placeholder {
+  color: #999;
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+  background-color: #eee;
+  opacity: 1;
+}
+.form-control[disabled],
+fieldset[disabled] .form-control {
+  cursor: not-allowed;
+}
+textarea.form-control {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-appearance: none;
+}
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
+  input[type="date"].form-control,
+  input[type="time"].form-control,
+  input[type="datetime-local"].form-control,
+  input[type="month"].form-control {
+    line-height: 34px;
+  }
+  input[type="date"].input-sm,
+  input[type="time"].input-sm,
+  input[type="datetime-local"].input-sm,
+  input[type="month"].input-sm,
+  .input-group-sm input[type="date"],
+  .input-group-sm input[type="time"],
+  .input-group-sm input[type="datetime-local"],
+  .input-group-sm input[type="month"] {
+    line-height: 30px;
+  }
+  input[type="date"].input-lg,
+  input[type="time"].input-lg,
+  input[type="datetime-local"].input-lg,
+  input[type="month"].input-lg,
+  .input-group-lg input[type="date"],
+  .input-group-lg input[type="time"],
+  .input-group-lg input[type="datetime-local"],
+  .input-group-lg input[type="month"] {
+    line-height: 46px;
+  }
+}
+.form-group {
+  margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+  position: relative;
+  display: block;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+  min-height: 20px;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+  position: absolute;
+  margin-top: 4px \9;
+  margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+  margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+  position: relative;
+  display: inline-block;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  vertical-align: middle;
+  cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+  margin-top: 0;
+  margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+  cursor: not-allowed;
+}
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+  cursor: not-allowed;
+}
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox label {
+  cursor: not-allowed;
+}
+.form-control-static {
+  min-height: 34px;
+  padding-top: 7px;
+  padding-bottom: 7px;
+  margin-bottom: 0;
+}
+.form-control-static.input-lg,
+.form-control-static.input-sm {
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-sm {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-sm {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-sm,
+select[multiple].input-sm {
+  height: auto;
+}
+.form-group-sm .form-control {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.form-group-sm select.form-control {
+  height: 30px;
+  line-height: 30px;
+}
+.form-group-sm textarea.form-control,
+.form-group-sm select[multiple].form-control {
+  height: auto;
+}
+.form-group-sm .form-control-static {
+  height: 30px;
+  min-height: 32px;
+  padding: 6px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+}
+.input-lg {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+select.input-lg {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-lg,
+select[multiple].input-lg {
+  height: auto;
+}
+.form-group-lg .form-control {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+.form-group-lg select.form-control {
+  height: 46px;
+  line-height: 46px;
+}
+.form-group-lg textarea.form-control,
+.form-group-lg select[multiple].form-control {
+  height: auto;
+}
+.form-group-lg .form-control-static {
+  height: 46px;
+  min-height: 38px;
+  padding: 11px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+}
+.has-feedback {
+  position: relative;
+}
+.has-feedback .form-control {
+  padding-right: 42.5px;
+}
+.form-control-feedback {
+  position: absolute;
+  top: 0;
+  right: 0;
+  z-index: 2;
+  display: block;
+  width: 34px;
+  height: 34px;
+  line-height: 34px;
+  text-align: center;
+  pointer-events: none;
+}
+.input-lg + .form-control-feedback,
+.input-group-lg + .form-control-feedback,
+.form-group-lg .form-control + .form-control-feedback {
+  width: 46px;
+  height: 46px;
+  line-height: 46px;
+}
+.input-sm + .form-control-feedback,
+.input-group-sm + .form-control-feedback,
+.form-group-sm .form-control + .form-control-feedback {
+  width: 30px;
+  height: 30px;
+  line-height: 30px;
+}
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline,
+.has-success.radio label,
+.has-success.checkbox label,
+.has-success.radio-inline label,
+.has-success.checkbox-inline label {
+  color: #3c763d;
+}
+.has-success .form-control {
+  border-color: #3c763d;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-success .form-control:focus {
+  border-color: #2b542c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+}
+.has-success .input-group-addon {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #3c763d;
+}
+.has-success .form-control-feedback {
+  color: #3c763d;
+}
+.has-warning .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline,
+.has-warning.radio label,
+.has-warning.checkbox label,
+.has-warning.radio-inline label,
+.has-warning.checkbox-inline label {
+  color: #8a6d3b;
+}
+.has-warning .form-control {
+  border-color: #8a6d3b;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-warning .form-control:focus {
+  border-color: #66512c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+}
+.has-warning .input-group-addon {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #8a6d3b;
+}
+.has-warning .form-control-feedback {
+  color: #8a6d3b;
+}
+.has-error .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline,
+.has-error.radio label,
+.has-error.checkbox label,
+.has-error.radio-inline label,
+.has-error.checkbox-inline label {
+  color: #a94442;
+}
+.has-error .form-control {
+  border-color: #a94442;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-error .form-control:focus {
+  border-color: #843534;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+}
+.has-error .input-group-addon {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #a94442;
+}
+.has-error .form-control-feedback {
+  color: #a94442;
+}
+.has-feedback label ~ .form-control-feedback {
+  top: 25px;
+}
+.has-feedback label.sr-only ~ .form-control-feedback {
+  top: 0;
+}
+.help-block {
+  display: block;
+  margin-top: 5px;
+  margin-bottom: 10px;
+  color: #737373;
+}
+@media (min-width: 768px) {
+  .form-inline .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .form-inline .form-control-static {
+    display: inline-block;
+  }
+  .form-inline .input-group {
+    display: inline-table;
+    vertical-align: middle;
+  }
+  .form-inline .input-group .input-group-addon,
+  .form-inline .input-group .input-group-btn,
+  .form-inline .input-group .form-control {
+    width: auto;
+  }
+  .form-inline .input-group > .form-control {
+    width: 100%;
+  }
+  .form-inline .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio,
+  .form-inline .checkbox {
+    display: inline-block;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio label,
+  .form-inline .checkbox label {
+    padding-left: 0;
+  }
+  .form-inline .radio input[type="radio"],
+  .form-inline .checkbox input[type="checkbox"] {
+    position: relative;
+    margin-left: 0;
+  }
+  .form-inline .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+  padding-top: 7px;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+  min-height: 27px;
+}
+.form-horizontal .form-group {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .control-label {
+    padding-top: 7px;
+    margin-bottom: 0;
+    text-align: right;
+  }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+  right: 15px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .form-group-lg .control-label {
+    padding-top: 14.333333px;
+    font-size: 18px;
+  }
+}
+@media (min-width: 768px) {
+  .form-horizontal .form-group-sm .control-label {
+    padding-top: 6px;
+    font-size: 12px;
+  }
+}
+.btn {
+  display: inline-block;
+  padding: 6px 12px;
+  margin-bottom: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  -ms-touch-action: manipulation;
+      touch-action: manipulation;
+  cursor: pointer;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus,
+.btn.focus {
+  color: #333;
+  text-decoration: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+  outline: 0;
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+  cursor: not-allowed;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  opacity: .65;
+}
+a.btn.disabled,
+fieldset[disabled] a.btn {
+  pointer-events: none;
+}
+.btn-default {
+  color: #333;
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default:focus,
+.btn-default.focus {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #8c8c8c;
+}
+.btn-default:hover {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #adadad;
+}
+.btn-default:active:hover,
+.btn-default.active:hover,
+.open > .dropdown-toggle.btn-default:hover,
+.btn-default:active:focus,
+.btn-default.active:focus,
+.open > .dropdown-toggle.btn-default:focus,
+.btn-default:active.focus,
+.btn-default.active.focus,
+.open > .dropdown-toggle.btn-default.focus {
+  color: #333;
+  background-color: #d4d4d4;
+  border-color: #8c8c8c;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  background-image: none;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default .badge {
+  color: #fff;
+  background-color: #333;
+}
+.btn-primary {
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #2e6da4;
+}
+.btn-primary:focus,
+.btn-primary.focus {
+  color: #fff;
+  background-color: #286090;
+  border-color: #122b40;
+}
+.btn-primary:hover {
+  color: #fff;
+  background-color: #286090;
+  border-color: #204d74;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  color: #fff;
+  background-color: #286090;
+  border-color: #204d74;
+}
+.btn-primary:active:hover,
+.btn-primary.active:hover,
+.open > .dropdown-toggle.btn-primary:hover,
+.btn-primary:active:focus,
+.btn-primary.active:focus,
+.open > .dropdown-toggle.btn-primary:focus,
+.btn-primary:active.focus,
+.btn-primary.active.focus,
+.open > .dropdown-toggle.btn-primary.focus {
+  color: #fff;
+  background-color: #204d74;
+  border-color: #122b40;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  background-image: none;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #337ab7;
+  border-color: #2e6da4;
+}
+.btn-primary .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.btn-success {
+  color: #fff;
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success:focus,
+.btn-success.focus {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #255625;
+}
+.btn-success:hover {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #398439;
+}
+.btn-success:active:hover,
+.btn-success.active:hover,
+.open > .dropdown-toggle.btn-success:hover,
+.btn-success:active:focus,
+.btn-success.active:focus,
+.open > .dropdown-toggle.btn-success:focus,
+.btn-success:active.focus,
+.btn-success.active.focus,
+.open > .dropdown-toggle.btn-success.focus {
+  color: #fff;
+  background-color: #398439;
+  border-color: #255625;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  background-image: none;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success .badge {
+  color: #5cb85c;
+  background-color: #fff;
+}
+.btn-info {
+  color: #fff;
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info:focus,
+.btn-info.focus {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #1b6d85;
+}
+.btn-info:hover {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #269abc;
+}
+.btn-info:active:hover,
+.btn-info.active:hover,
+.open > .dropdown-toggle.btn-info:hover,
+.btn-info:active:focus,
+.btn-info.active:focus,
+.open > .dropdown-toggle.btn-info:focus,
+.btn-info:active.focus,
+.btn-info.active.focus,
+.open > .dropdown-toggle.btn-info.focus {
+  color: #fff;
+  background-color: #269abc;
+  border-color: #1b6d85;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  background-image: none;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info .badge {
+  color: #5bc0de;
+  background-color: #fff;
+}
+.btn-warning {
+  color: #fff;
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning:focus,
+.btn-warning.focus {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #985f0d;
+}
+.btn-warning:hover {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #d58512;
+}
+.btn-warning:active:hover,
+.btn-warning.active:hover,
+.open > .dropdown-toggle.btn-warning:hover,
+.btn-warning:active:focus,
+.btn-warning.active:focus,
+.open > .dropdown-toggle.btn-warning:focus,
+.btn-warning:active.focus,
+.btn-warning.active.focus,
+.open > .dropdown-toggle.btn-warning.focus {
+  color: #fff;
+  background-color: #d58512;
+  border-color: #985f0d;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  background-image: none;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning .badge {
+  color: #f0ad4e;
+  background-color: #fff;
+}
+.btn-danger {
+  color: #fff;
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger:focus,
+.btn-danger.focus {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #761c19;
+}
+.btn-danger:hover {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #ac2925;
+}
+.btn-danger:active:hover,
+.btn-danger.active:hover,
+.open > .dropdown-toggle.btn-danger:hover,
+.btn-danger:active:focus,
+.btn-danger.active:focus,
+.open > .dropdown-toggle.btn-danger:focus,
+.btn-danger:active.focus,
+.btn-danger.active.focus,
+.open > .dropdown-toggle.btn-danger.focus {
+  color: #fff;
+  background-color: #ac2925;
+  border-color: #761c19;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  background-image: none;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger .badge {
+  color: #d9534f;
+  background-color: #fff;
+}
+.btn-link {
+  font-weight: normal;
+  color: #337ab7;
+  border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link.active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+  background-color: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+  border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+  color: #23527c;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+  color: #777;
+  text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+  padding: 1px 5px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-block {
+  display: block;
+  width: 100%;
+}
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+  width: 100%;
+}
+.fade {
+  opacity: 0;
+  -webkit-transition: opacity .15s linear;
+       -o-transition: opacity .15s linear;
+          transition: opacity .15s linear;
+}
+.fade.in {
+  opacity: 1;
+}
+.collapse {
+  display: none;
+}
+.collapse.in {
+  display: block;
+}
+tr.collapse.in {
+  display: table-row;
+}
+tbody.collapse.in {
+  display: table-row-group;
+}
+.collapsing {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  -webkit-transition-timing-function: ease;
+       -o-transition-timing-function: ease;
+          transition-timing-function: ease;
+  -webkit-transition-duration: .35s;
+       -o-transition-duration: .35s;
+          transition-duration: .35s;
+  -webkit-transition-property: height, visibility;
+       -o-transition-property: height, visibility;
+          transition-property: height, visibility;
+}
+.caret {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  margin-left: 2px;
+  vertical-align: middle;
+  border-top: 4px dashed;
+  border-top: 4px solid \9;
+  border-right: 4px solid transparent;
+  border-left: 4px solid transparent;
+}
+.dropup,
+.dropdown {
+  position: relative;
+}
+.dropdown-toggle:focus {
+  outline: 0;
+}
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  margin: 2px 0 0;
+  font-size: 14px;
+  text-align: left;
+  list-style: none;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .15);
+  border-radius: 4px;
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+          box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+}
+.dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu .divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.dropdown-menu > li > a {
+  display: block;
+  padding: 3px 20px;
+  clear: both;
+  font-weight: normal;
+  line-height: 1.42857143;
+  color: #333;
+  white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  color: #262626;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  color: #fff;
+  text-decoration: none;
+  background-color: #337ab7;
+  outline: 0;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  color: #777;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+  background-image: none;
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.open > .dropdown-menu {
+  display: block;
+}
+.open > a {
+  outline: 0;
+}
+.dropdown-menu-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu-left {
+  right: auto;
+  left: 0;
+}
+.dropdown-header {
+  display: block;
+  padding: 3px 20px;
+  font-size: 12px;
+  line-height: 1.42857143;
+  color: #777;
+  white-space: nowrap;
+}
+.dropdown-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 990;
+}
+.pull-right > .dropdown-menu {
+  right: 0;
+  left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+  content: "";
+  border-top: 0;
+  border-bottom: 4px dashed;
+  border-bottom: 4px solid \9;
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+  top: auto;
+  bottom: 100%;
+  margin-bottom: 2px;
+}
+@media (min-width: 768px) {
+  .navbar-right .dropdown-menu {
+    right: 0;
+    left: auto;
+  }
+  .navbar-right .dropdown-menu-left {
+    right: auto;
+    left: 0;
+  }
+}
+.btn-group,
+.btn-group-vertical {
+  position: relative;
+  display: inline-block;
+  vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+  position: relative;
+  float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+  z-index: 2;
+}
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+  margin-left: -1px;
+}
+.btn-toolbar {
+  margin-left: -5px;
+}
+.btn-toolbar .btn,
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+  float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+  margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+  border-radius: 0;
+}
+.btn-group > .btn:first-child {
+  margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group > .btn-group {
+  float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+  outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+  padding-right: 8px;
+  padding-left: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+  padding-right: 12px;
+  padding-left: 12px;
+}
+.btn-group.open .dropdown-toggle {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn .caret {
+  margin-left: 0;
+}
+.btn-lg .caret {
+  border-width: 5px 5px 0;
+  border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+  border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
+  display: block;
+  float: none;
+  width: 100%;
+  max-width: 100%;
+}
+.btn-group-vertical > .btn-group > .btn {
+  float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+  margin-top: -1px;
+  margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+  border-bottom-left-radius: 4px;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.btn-group-justified {
+  display: table;
+  width: 100%;
+  table-layout: fixed;
+  border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+  display: table-cell;
+  float: none;
+  width: 1%;
+}
+.btn-group-justified > .btn-group .btn {
+  width: 100%;
+}
+.btn-group-justified > .btn-group .dropdown-menu {
+  left: auto;
+}
+[data-toggle="buttons"] > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn input[type="checkbox"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
+  position: absolute;
+  clip: rect(0, 0, 0, 0);
+  pointer-events: none;
+}
+.input-group {
+  position: relative;
+  display: table;
+  border-collapse: separate;
+}
+.input-group[class*="col-"] {
+  float: none;
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-group .form-control {
+  position: relative;
+  z-index: 2;
+  float: left;
+  width: 100%;
+  margin-bottom: 0;
+}
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].input-group-lg > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-group-sm > .form-control,
+select.input-group-sm > .input-group-addon,
+select.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+  display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.input-group-addon,
+.input-group-btn {
+  width: 1%;
+  white-space: nowrap;
+  vertical-align: middle;
+}
+.input-group-addon {
+  padding: 6px 12px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1;
+  color: #555;
+  text-align: center;
+  background-color: #eee;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+.input-group-addon.input-sm {
+  padding: 5px 10px;
+  font-size: 12px;
+  border-radius: 3px;
+}
+.input-group-addon.input-lg {
+  padding: 10px 16px;
+  font-size: 18px;
+  border-radius: 6px;
+}
+.input-group-addon input[type="radio"],
+.input-group-addon input[type="checkbox"] {
+  margin-top: 0;
+}
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.input-group-addon:first-child {
+  border-right: 0;
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.input-group-addon:last-child {
+  border-left: 0;
+}
+.input-group-btn {
+  position: relative;
+  font-size: 0;
+  white-space: nowrap;
+}
+.input-group-btn > .btn {
+  position: relative;
+}
+.input-group-btn > .btn + .btn {
+  margin-left: -1px;
+}
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+  z-index: 2;
+}
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group {
+  margin-right: -1px;
+}
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group {
+  z-index: 2;
+  margin-left: -1px;
+}
+.nav {
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+.nav > li {
+  position: relative;
+  display: block;
+}
+.nav > li > a {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.nav > li.disabled > a {
+  color: #777;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+  color: #777;
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+  background-color: #eee;
+  border-color: #337ab7;
+}
+.nav .nav-divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.nav > li > a > img {
+  max-width: none;
+}
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+.nav-tabs > li {
+  float: left;
+  margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+  margin-right: 2px;
+  line-height: 1.42857143;
+  border: 1px solid transparent;
+  border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+  border-color: #eee #eee #ddd;
+}
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+  color: #555;
+  cursor: default;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+.nav-tabs.nav-justified {
+  width: 100%;
+  border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+  float: none;
+}
+.nav-tabs.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-tabs.nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs.nav-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs.nav-justified > .active > a,
+.nav-tabs.nav-justified > .active > a:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs.nav-justified > .active > a,
+  .nav-tabs.nav-justified > .active > a:hover,
+  .nav-tabs.nav-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.nav-pills > li {
+  float: left;
+}
+.nav-pills > li > a {
+  border-radius: 4px;
+}
+.nav-pills > li + li {
+  margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+  color: #fff;
+  background-color: #337ab7;
+}
+.nav-stacked > li {
+  float: none;
+}
+.nav-stacked > li + li {
+  margin-top: 2px;
+  margin-left: 0;
+}
+.nav-justified {
+  width: 100%;
+}
+.nav-justified > li {
+  float: none;
+}
+.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs-justified {
+  border-bottom: 0;
+}
+.nav-tabs-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs-justified > .active > a,
+.nav-tabs-justified > .active > a:hover,
+.nav-tabs-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs-justified > .active > a,
+  .nav-tabs-justified > .active > a:hover,
+  .nav-tabs-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.tab-content > .tab-pane {
+  display: none;
+}
+.tab-content > .active {
+  display: block;
+}
+.nav-tabs .dropdown-menu {
+  margin-top: -1px;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar {
+  position: relative;
+  min-height: 50px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+}
+@media (min-width: 768px) {
+  .navbar {
+    border-radius: 4px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-header {
+    float: left;
+  }
+}
+.navbar-collapse {
+  padding-right: 15px;
+  padding-left: 15px;
+  overflow-x: visible;
+  -webkit-overflow-scrolling: touch;
+  border-top: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+}
+.navbar-collapse.in {
+  overflow-y: auto;
+}
+@media (min-width: 768px) {
+  .navbar-collapse {
+    width: auto;
+    border-top: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-collapse.collapse {
+    display: block !important;
+    height: auto !important;
+    padding-bottom: 0;
+    overflow: visible !important;
+  }
+  .navbar-collapse.in {
+    overflow-y: visible;
+  }
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-static-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+.navbar-fixed-top .navbar-collapse,
+.navbar-fixed-bottom .navbar-collapse {
+  max-height: 340px;
+}
+@media (max-device-width: 480px) and (orientation: landscape) {
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    max-height: 200px;
+  }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .container > .navbar-header,
+  .container-fluid > .navbar-header,
+  .container > .navbar-collapse,
+  .container-fluid > .navbar-collapse {
+    margin-right: 0;
+    margin-left: 0;
+  }
+}
+.navbar-static-top {
+  z-index: 1000;
+  border-width: 0 0 1px;
+}
+@media (min-width: 768px) {
+  .navbar-static-top {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: 1030;
+}
+@media (min-width: 768px) {
+  .navbar-fixed-top,
+  .navbar-fixed-bottom {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top {
+  top: 0;
+  border-width: 0 0 1px;
+}
+.navbar-fixed-bottom {
+  bottom: 0;
+  margin-bottom: 0;
+  border-width: 1px 0 0;
+}
+.navbar-brand {
+  float: left;
+  height: 50px;
+  padding: 15px 15px;
+  font-size: 18px;
+  line-height: 20px;
+}
+.navbar-brand:hover,
+.navbar-brand:focus {
+  text-decoration: none;
+}
+.navbar-brand > img {
+  display: block;
+}
+@media (min-width: 768px) {
+  .navbar > .container .navbar-brand,
+  .navbar > .container-fluid .navbar-brand {
+    margin-left: -15px;
+  }
+}
+.navbar-toggle {
+  position: relative;
+  float: right;
+  padding: 9px 10px;
+  margin-top: 8px;
+  margin-right: 15px;
+  margin-bottom: 8px;
+  background-color: transparent;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.navbar-toggle:focus {
+  outline: 0;
+}
+.navbar-toggle .icon-bar {
+  display: block;
+  width: 22px;
+  height: 2px;
+  border-radius: 1px;
+}
+.navbar-toggle .icon-bar + .icon-bar {
+  margin-top: 4px;
+}
+@media (min-width: 768px) {
+  .navbar-toggle {
+    display: none;
+  }
+}
+.navbar-nav {
+  margin: 7.5px -15px;
+}
+.navbar-nav > li > a {
+  padding-top: 10px;
+  padding-bottom: 10px;
+  line-height: 20px;
+}
+@media (max-width: 767px) {
+  .navbar-nav .open .dropdown-menu {
+    position: static;
+    float: none;
+    width: auto;
+    margin-top: 0;
+    background-color: transparent;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-nav .open .dropdown-menu > li > a,
+  .navbar-nav .open .dropdown-menu .dropdown-header {
+    padding: 5px 15px 5px 25px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a {
+    line-height: 20px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-nav .open .dropdown-menu > li > a:focus {
+    background-image: none;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-nav {
+    float: left;
+    margin: 0;
+  }
+  .navbar-nav > li {
+    float: left;
+  }
+  .navbar-nav > li > a {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+}
+.navbar-form {
+  padding: 10px 15px;
+  margin-top: 8px;
+  margin-right: -15px;
+  margin-bottom: 8px;
+  margin-left: -15px;
+  border-top: 1px solid transparent;
+  border-bottom: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+}
+@media (min-width: 768px) {
+  .navbar-form .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .navbar-form .form-control-static {
+    display: inline-block;
+  }
+  .navbar-form .input-group {
+    display: inline-table;
+    vertical-align: middle;
+  }
+  .navbar-form .input-group .input-group-addon,
+  .navbar-form .input-group .input-group-btn,
+  .navbar-form .input-group .form-control {
+    width: auto;
+  }
+  .navbar-form .input-group > .form-control {
+    width: 100%;
+  }
+  .navbar-form .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio,
+  .navbar-form .checkbox {
+    display: inline-block;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio label,
+  .navbar-form .checkbox label {
+    padding-left: 0;
+  }
+  .navbar-form .radio input[type="radio"],
+  .navbar-form .checkbox input[type="checkbox"] {
+    position: relative;
+    margin-left: 0;
+  }
+  .navbar-form .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+@media (max-width: 767px) {
+  .navbar-form .form-group {
+    margin-bottom: 5px;
+  }
+  .navbar-form .form-group:last-child {
+    margin-bottom: 0;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-form {
+    width: auto;
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-right: 0;
+    margin-left: 0;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+}
+.navbar-nav > li > .dropdown-menu {
+  margin-top: 0;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+  margin-bottom: 0;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.navbar-btn {
+  margin-top: 8px;
+  margin-bottom: 8px;
+}
+.navbar-btn.btn-sm {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.navbar-btn.btn-xs {
+  margin-top: 14px;
+  margin-bottom: 14px;
+}
+.navbar-text {
+  margin-top: 15px;
+  margin-bottom: 15px;
+}
+@media (min-width: 768px) {
+  .navbar-text {
+    float: left;
+    margin-right: 15px;
+    margin-left: 15px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-left {
+    float: left !important;
+  }
+  .navbar-right {
+    float: right !important;
+    margin-right: -15px;
+  }
+  .navbar-right ~ .navbar-right {
+    margin-right: 0;
+  }
+}
+.navbar-default {
+  background-color: #f8f8f8;
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-brand {
+  color: #777;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+  color: #5e5e5e;
+  background-color: transparent;
+}
+.navbar-default .navbar-text {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+  color: #333;
+  background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+  color: #ccc;
+  background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+  border-color: #ddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+  background-color: #ddd;
+}
+.navbar-default .navbar-toggle .icon-bar {
+  background-color: #888;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+@media (max-width: 767px) {
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+    color: #777;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #333;
+    background-color: transparent;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #555;
+    background-color: #e7e7e7;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #ccc;
+    background-color: transparent;
+  }
+}
+.navbar-default .navbar-link {
+  color: #777;
+}
+.navbar-default .navbar-link:hover {
+  color: #333;
+}
+.navbar-default .btn-link {
+  color: #777;
+}
+.navbar-default .btn-link:hover,
+.navbar-default .btn-link:focus {
+  color: #333;
+}
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:hover,
+.navbar-default .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-default .btn-link:focus {
+  color: #ccc;
+}
+.navbar-inverse {
+  background-color: #222;
+  border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-text {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-nav > li > a {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+  color: #444;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+  border-color: #333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+  background-color: #333;
+}
+.navbar-inverse .navbar-toggle .icon-bar {
+  background-color: #fff;
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+  border-color: #101010;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+@media (max-width: 767px) {
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+    border-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+    color: #9d9d9d;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #fff;
+    background-color: transparent;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #444;
+    background-color: transparent;
+  }
+}
+.navbar-inverse .navbar-link {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-link:hover {
+  color: #fff;
+}
+.navbar-inverse .btn-link {
+  color: #9d9d9d;
+}
+.navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link:focus {
+  color: #fff;
+}
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-inverse .btn-link:focus {
+  color: #444;
+}
+.breadcrumb {
+  padding: 8px 15px;
+  margin-bottom: 20px;
+  list-style: none;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+.breadcrumb > li {
+  display: inline-block;
+}
+.breadcrumb > li + li:before {
+  padding: 0 5px;
+  color: #ccc;
+  content: "/\00a0";
+}
+.breadcrumb > .active {
+  color: #777;
+}
+.pagination {
+  display: inline-block;
+  padding-left: 0;
+  margin: 20px 0;
+  border-radius: 4px;
+}
+.pagination > li {
+  display: inline;
+}
+.pagination > li > a,
+.pagination > li > span {
+  position: relative;
+  float: left;
+  padding: 6px 12px;
+  margin-left: -1px;
+  line-height: 1.42857143;
+  color: #337ab7;
+  text-decoration: none;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+  margin-left: 0;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+  z-index: 3;
+  color: #23527c;
+  background-color: #eee;
+  border-color: #ddd;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+  z-index: 2;
+  color: #fff;
+  cursor: default;
+  background-color: #337ab7;
+  border-color: #337ab7;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #fff;
+  border-color: #ddd;
+}
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+}
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+  border-top-left-radius: 6px;
+  border-bottom-left-radius: 6px;
+}
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+  border-top-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+}
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+}
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+  border-top-left-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+  border-top-right-radius: 3px;
+  border-bottom-right-radius: 3px;
+}
+.pager {
+  padding-left: 0;
+  margin: 20px 0;
+  text-align: center;
+  list-style: none;
+}
+.pager li {
+  display: inline;
+}
+.pager li > a,
+.pager li > span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 15px;
+}
+.pager li > a:hover,
+.pager li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.pager .next > a,
+.pager .next > span {
+  float: right;
+}
+.pager .previous > a,
+.pager .previous > span {
+  float: left;
+}
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #fff;
+}
+.label {
+  display: inline;
+  padding: .2em .6em .3em;
+  font-size: 75%;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: .25em;
+}
+a.label:hover,
+a.label:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+.label:empty {
+  display: none;
+}
+.btn .label {
+  position: relative;
+  top: -1px;
+}
+.label-default {
+  background-color: #777;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+  background-color: #5e5e5e;
+}
+.label-primary {
+  background-color: #337ab7;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+  background-color: #286090;
+}
+.label-success {
+  background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+  background-color: #449d44;
+}
+.label-info {
+  background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+  background-color: #31b0d5;
+}
+.label-warning {
+  background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+  background-color: #ec971f;
+}
+.label-danger {
+  background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+  background-color: #c9302c;
+}
+.badge {
+  display: inline-block;
+  min-width: 10px;
+  padding: 3px 7px;
+  font-size: 12px;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  background-color: #777;
+  border-radius: 10px;
+}
+.badge:empty {
+  display: none;
+}
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+.btn-xs .badge,
+.btn-group-xs > .btn .badge {
+  top: 0;
+  padding: 1px 5px;
+}
+a.badge:hover,
+a.badge:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.list-group-item > .badge {
+  float: right;
+}
+.list-group-item > .badge + .badge {
+  margin-right: 5px;
+}
+.nav-pills > li > a > .badge {
+  margin-left: 3px;
+}
+.jumbotron {
+  padding-top: 30px;
+  padding-bottom: 30px;
+  margin-bottom: 30px;
+  color: inherit;
+  background-color: #eee;
+}
+.jumbotron h1,
+.jumbotron .h1 {
+  color: inherit;
+}
+.jumbotron p {
+  margin-bottom: 15px;
+  font-size: 21px;
+  font-weight: 200;
+}
+.jumbotron > hr {
+  border-top-color: #d5d5d5;
+}
+.container .jumbotron,
+.container-fluid .jumbotron {
+  border-radius: 6px;
+}
+.jumbotron .container {
+  max-width: 100%;
+}
+@media screen and (min-width: 768px) {
+  .jumbotron {
+    padding-top: 48px;
+    padding-bottom: 48px;
+  }
+  .container .jumbotron,
+  .container-fluid .jumbotron {
+    padding-right: 60px;
+    padding-left: 60px;
+  }
+  .jumbotron h1,
+  .jumbotron .h1 {
+    font-size: 63px;
+  }
+}
+.thumbnail {
+  display: block;
+  padding: 4px;
+  margin-bottom: 20px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: border .2s ease-in-out;
+       -o-transition: border .2s ease-in-out;
+          transition: border .2s ease-in-out;
+}
+.thumbnail > img,
+.thumbnail a > img {
+  margin-right: auto;
+  margin-left: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+  border-color: #337ab7;
+}
+.thumbnail .caption {
+  padding: 9px;
+  color: #333;
+}
+.alert {
+  padding: 15px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.alert h4 {
+  margin-top: 0;
+  color: inherit;
+}
+.alert .alert-link {
+  font-weight: bold;
+}
+.alert > p,
+.alert > ul {
+  margin-bottom: 0;
+}
+.alert > p + p {
+  margin-top: 5px;
+}
+.alert-dismissable,
+.alert-dismissible {
+  padding-right: 35px;
+}
+.alert-dismissable .close,
+.alert-dismissible .close {
+  position: relative;
+  top: -2px;
+  right: -21px;
+  color: inherit;
+}
+.alert-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.alert-success hr {
+  border-top-color: #c9e2b3;
+}
+.alert-success .alert-link {
+  color: #2b542c;
+}
+.alert-info {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.alert-info hr {
+  border-top-color: #a6e1ec;
+}
+.alert-info .alert-link {
+  color: #245269;
+}
+.alert-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.alert-warning hr {
+  border-top-color: #f7e1b5;
+}
+.alert-warning .alert-link {
+  color: #66512c;
+}
+.alert-danger {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.alert-danger hr {
+  border-top-color: #e4b9c0;
+}
+.alert-danger .alert-link {
+  color: #843534;
+}
+@-webkit-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@-o-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+.progress {
+  height: 20px;
+  margin-bottom: 20px;
+  overflow: hidden;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+}
+.progress-bar {
+  float: left;
+  width: 0;
+  height: 100%;
+  font-size: 12px;
+  line-height: 20px;
+  color: #fff;
+  text-align: center;
+  background-color: #337ab7;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+  -webkit-transition: width .6s ease;
+       -o-transition: width .6s ease;
+          transition: width .6s ease;
+}
+.progress-striped .progress-bar,
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  -webkit-background-size: 40px 40px;
+          background-size: 40px 40px;
+}
+.progress.active .progress-bar,
+.progress-bar.active {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar-success {
+  background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+  background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+  background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+  background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.media {
+  margin-top: 15px;
+}
+.media:first-child {
+  margin-top: 0;
+}
+.media,
+.media-body {
+  overflow: hidden;
+  zoom: 1;
+}
+.media-body {
+  width: 10000px;
+}
+.media-object {
+  display: block;
+}
+.media-object.img-thumbnail {
+  max-width: none;
+}
+.media-right,
+.media > .pull-right {
+  padding-left: 10px;
+}
+.media-left,
+.media > .pull-left {
+  padding-right: 10px;
+}
+.media-left,
+.media-right,
+.media-body {
+  display: table-cell;
+  vertical-align: top;
+}
+.media-middle {
+  vertical-align: middle;
+}
+.media-bottom {
+  vertical-align: bottom;
+}
+.media-heading {
+  margin-top: 0;
+  margin-bottom: 5px;
+}
+.media-list {
+  padding-left: 0;
+  list-style: none;
+}
+.list-group {
+  padding-left: 0;
+  margin-bottom: 20px;
+}
+.list-group-item {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+  margin-bottom: -1px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.list-group-item:first-child {
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+.list-group-item:last-child {
+  margin-bottom: 0;
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+a.list-group-item,
+button.list-group-item {
+  color: #555;
+}
+a.list-group-item .list-group-item-heading,
+button.list-group-item .list-group-item-heading {
+  color: #333;
+}
+a.list-group-item:hover,
+button.list-group-item:hover,
+a.list-group-item:focus,
+button.list-group-item:focus {
+  color: #555;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+button.list-group-item {
+  width: 100%;
+  text-align: left;
+}
+.list-group-item.disabled,
+.list-group-item.disabled:hover,
+.list-group-item.disabled:focus {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #eee;
+}
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading {
+  color: inherit;
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text {
+  color: #777;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  z-index: 2;
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #337ab7;
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active .list-group-item-heading > small,
+.list-group-item.active:hover .list-group-item-heading > small,
+.list-group-item.active:focus .list-group-item-heading > small,
+.list-group-item.active .list-group-item-heading > .small,
+.list-group-item.active:hover .list-group-item-heading > .small,
+.list-group-item.active:focus .list-group-item-heading > .small {
+  color: inherit;
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+  color: #c7ddef;
+}
+.list-group-item-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+}
+a.list-group-item-success,
+button.list-group-item-success {
+  color: #3c763d;
+}
+a.list-group-item-success .list-group-item-heading,
+button.list-group-item-success .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-success:hover,
+button.list-group-item-success:hover,
+a.list-group-item-success:focus,
+button.list-group-item-success:focus {
+  color: #3c763d;
+  background-color: #d0e9c6;
+}
+a.list-group-item-success.active,
+button.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+button.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus,
+button.list-group-item-success.active:focus {
+  color: #fff;
+  background-color: #3c763d;
+  border-color: #3c763d;
+}
+.list-group-item-info {
+  color: #31708f;
+  background-color: #d9edf7;
+}
+a.list-group-item-info,
+button.list-group-item-info {
+  color: #31708f;
+}
+a.list-group-item-info .list-group-item-heading,
+button.list-group-item-info .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-info:hover,
+button.list-group-item-info:hover,
+a.list-group-item-info:focus,
+button.list-group-item-info:focus {
+  color: #31708f;
+  background-color: #c4e3f3;
+}
+a.list-group-item-info.active,
+button.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+button.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus,
+button.list-group-item-info.active:focus {
+  color: #fff;
+  background-color: #31708f;
+  border-color: #31708f;
+}
+.list-group-item-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+}
+a.list-group-item-warning,
+button.list-group-item-warning {
+  color: #8a6d3b;
+}
+a.list-group-item-warning .list-group-item-heading,
+button.list-group-item-warning .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-warning:hover,
+button.list-group-item-warning:hover,
+a.list-group-item-warning:focus,
+button.list-group-item-warning:focus {
+  color: #8a6d3b;
+  background-color: #faf2cc;
+}
+a.list-group-item-warning.active,
+button.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+button.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus,
+button.list-group-item-warning.active:focus {
+  color: #fff;
+  background-color: #8a6d3b;
+  border-color: #8a6d3b;
+}
+.list-group-item-danger {
+  color: #a94442;
+  background-color: #f2dede;
+}
+a.list-group-item-danger,
+button.list-group-item-danger {
+  color: #a94442;
+}
+a.list-group-item-danger .list-group-item-heading,
+button.list-group-item-danger .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-danger:hover,
+button.list-group-item-danger:hover,
+a.list-group-item-danger:focus,
+button.list-group-item-danger:focus {
+  color: #a94442;
+  background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+button.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+button.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus,
+button.list-group-item-danger.active:focus {
+  color: #fff;
+  background-color: #a94442;
+  border-color: #a94442;
+}
+.list-group-item-heading {
+  margin-top: 0;
+  margin-bottom: 5px;
+}
+.list-group-item-text {
+  margin-bottom: 0;
+  line-height: 1.3;
+}
+.panel {
+  margin-bottom: 20px;
+  background-color: #fff;
+  border: 1px solid transparent;
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+}
+.panel-body {
+  padding: 15px;
+}
+.panel-heading {
+  padding: 10px 15px;
+  border-bottom: 1px solid transparent;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+  color: inherit;
+}
+.panel-title {
+  margin-top: 0;
+  margin-bottom: 0;
+  font-size: 16px;
+  color: inherit;
+}
+.panel-title > a,
+.panel-title > small,
+.panel-title > .small,
+.panel-title > small > a,
+.panel-title > .small > a {
+  color: inherit;
+}
+.panel-footer {
+  padding: 10px 15px;
+  background-color: #f5f5f5;
+  border-top: 1px solid #ddd;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .list-group,
+.panel > .panel-collapse > .list-group {
+  margin-bottom: 0;
+}
+.panel > .list-group .list-group-item,
+.panel > .panel-collapse > .list-group .list-group-item {
+  border-width: 1px 0;
+  border-radius: 0;
+}
+.panel > .list-group:first-child .list-group-item:first-child,
+.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {
+  border-top: 0;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .list-group:last-child .list-group-item:last-child,
+.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {
+  border-bottom: 0;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+  border-top-width: 0;
+}
+.list-group + .panel-footer {
+  border-top-width: 0;
+}
+.panel > .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+  margin-bottom: 0;
+}
+.panel > .table caption,
+.panel > .table-responsive > .table caption,
+.panel > .panel-collapse > .table caption {
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+  border-top-left-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+  border-top-right-radius: 3px;
+}
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
+  border-bottom-right-radius: 3px;
+}
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive,
+.panel > .table + .panel-body,
+.panel > .table-responsive + .panel-body {
+  border-top: 1px solid #ddd;
+}
+.panel > .table > tbody:first-child > tr:first-child th,
+.panel > .table > tbody:first-child > tr:first-child td {
+  border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+  border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+  border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+  border-right: 0;
+}
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+  border-bottom: 0;
+}
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
+  border-bottom: 0;
+}
+.panel > .table-responsive {
+  margin-bottom: 0;
+  border: 0;
+}
+.panel-group {
+  margin-bottom: 20px;
+}
+.panel-group .panel {
+  margin-bottom: 0;
+  border-radius: 4px;
+}
+.panel-group .panel + .panel {
+  margin-top: 5px;
+}
+.panel-group .panel-heading {
+  border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse > .panel-body,
+.panel-group .panel-heading + .panel-collapse > .list-group {
+  border-top: 1px solid #ddd;
+}
+.panel-group .panel-footer {
+  border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+  border-bottom: 1px solid #ddd;
+}
+.panel-default {
+  border-color: #ddd;
+}
+.panel-default > .panel-heading {
+  color: #333;
+  background-color: #f5f5f5;
+  border-color: #ddd;
+}
+.panel-default > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ddd;
+}
+.panel-default > .panel-heading .badge {
+  color: #f5f5f5;
+  background-color: #333;
+}
+.panel-default > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ddd;
+}
+.panel-primary {
+  border-color: #337ab7;
+}
+.panel-primary > .panel-heading {
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #337ab7;
+}
+.panel-primary > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #337ab7;
+}
+.panel-primary > .panel-heading .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.panel-primary > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #337ab7;
+}
+.panel-success {
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #d6e9c6;
+}
+.panel-success > .panel-heading .badge {
+  color: #dff0d8;
+  background-color: #3c763d;
+}
+.panel-success > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #d6e9c6;
+}
+.panel-info {
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #bce8f1;
+}
+.panel-info > .panel-heading .badge {
+  color: #d9edf7;
+  background-color: #31708f;
+}
+.panel-info > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #bce8f1;
+}
+.panel-warning {
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #faebcc;
+}
+.panel-warning > .panel-heading .badge {
+  color: #fcf8e3;
+  background-color: #8a6d3b;
+}
+.panel-warning > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #faebcc;
+}
+.panel-danger {
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ebccd1;
+}
+.panel-danger > .panel-heading .badge {
+  color: #f2dede;
+  background-color: #a94442;
+}
+.panel-danger > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ebccd1;
+}
+.embed-responsive {
+  position: relative;
+  display: block;
+  height: 0;
+  padding: 0;
+  overflow: hidden;
+}
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object,
+.embed-responsive video {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  border: 0;
+}
+.embed-responsive-16by9 {
+  padding-bottom: 56.25%;
+}
+.embed-responsive-4by3 {
+  padding-bottom: 75%;
+}
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  border: 1px solid #e3e3e3;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+}
+.well blockquote {
+  border-color: #ddd;
+  border-color: rgba(0, 0, 0, .15);
+}
+.well-lg {
+  padding: 24px;
+  border-radius: 6px;
+}
+.well-sm {
+  padding: 9px;
+  border-radius: 3px;
+}
+.close {
+  float: right;
+  font-size: 21px;
+  font-weight: bold;
+  line-height: 1;
+  color: #000;
+  text-shadow: 0 1px 0 #fff;
+  filter: alpha(opacity=20);
+  opacity: .2;
+}
+.close:hover,
+.close:focus {
+  color: #000;
+  text-decoration: none;
+  cursor: pointer;
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+button.close {
+  -webkit-appearance: none;
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+}
+.modal-open {
+  overflow: hidden;
+}
+.modal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1050;
+  display: none;
+  overflow: hidden;
+  -webkit-overflow-scrolling: touch;
+  outline: 0;
+}
+.modal.fade .modal-dialog {
+  -webkit-transition: -webkit-transform .3s ease-out;
+       -o-transition:      -o-transform .3s ease-out;
+          transition:         transform .3s ease-out;
+  -webkit-transform: translate(0, -25%);
+      -ms-transform: translate(0, -25%);
+       -o-transform: translate(0, -25%);
+          transform: translate(0, -25%);
+}
+.modal.in .modal-dialog {
+  -webkit-transform: translate(0, 0);
+      -ms-transform: translate(0, 0);
+       -o-transform: translate(0, 0);
+          transform: translate(0, 0);
+}
+.modal-open .modal {
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: 10px;
+}
+.modal-content {
+  position: relative;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #999;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  outline: 0;
+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+          box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+}
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1040;
+  background-color: #000;
+}
+.modal-backdrop.fade {
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.modal-backdrop.in {
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.modal-header {
+  min-height: 16.42857143px;
+  padding: 15px;
+  border-bottom: 1px solid #e5e5e5;
+}
+.modal-header .close {
+  margin-top: -2px;
+}
+.modal-title {
+  margin: 0;
+  line-height: 1.42857143;
+}
+.modal-body {
+  position: relative;
+  padding: 15px;
+}
+.modal-footer {
+  padding: 15px;
+  text-align: right;
+  border-top: 1px solid #e5e5e5;
+}
+.modal-footer .btn + .btn {
+  margin-bottom: 0;
+  margin-left: 5px;
+}
+.modal-footer .btn-group .btn + .btn {
+  margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+  margin-left: 0;
+}
+.modal-scrollbar-measure {
+  position: absolute;
+  top: -9999px;
+  width: 50px;
+  height: 50px;
+  overflow: scroll;
+}
+@media (min-width: 768px) {
+  .modal-dialog {
+    width: 600px;
+    margin: 30px auto;
+  }
+  .modal-content {
+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+            box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+  }
+  .modal-sm {
+    width: 300px;
+  }
+}
+@media (min-width: 992px) {
+  .modal-lg {
+    width: 900px;
+  }
+}
+.tooltip {
+  position: absolute;
+  z-index: 1070;
+  display: block;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 12px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: left;
+  text-align: start;
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  letter-spacing: normal;
+  word-break: normal;
+  word-spacing: normal;
+  word-wrap: normal;
+  white-space: normal;
+  filter: alpha(opacity=0);
+  opacity: 0;
+
+  line-break: auto;
+}
+.tooltip.in {
+  filter: alpha(opacity=90);
+  opacity: .9;
+}
+.tooltip.top {
+  padding: 5px 0;
+  margin-top: -3px;
+}
+.tooltip.right {
+  padding: 0 5px;
+  margin-left: 3px;
+}
+.tooltip.bottom {
+  padding: 5px 0;
+  margin-top: 3px;
+}
+.tooltip.left {
+  padding: 0 5px;
+  margin-left: -3px;
+}
+.tooltip-inner {
+  max-width: 200px;
+  padding: 3px 8px;
+  color: #fff;
+  text-align: center;
+  background-color: #000;
+  border-radius: 4px;
+}
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.tooltip.top .tooltip-arrow {
+  bottom: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-left .tooltip-arrow {
+  right: 5px;
+  bottom: 0;
+  margin-bottom: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-right .tooltip-arrow {
+  bottom: 0;
+  left: 5px;
+  margin-bottom: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.right .tooltip-arrow {
+  top: 50%;
+  left: 0;
+  margin-top: -5px;
+  border-width: 5px 5px 5px 0;
+  border-right-color: #000;
+}
+.tooltip.left .tooltip-arrow {
+  top: 50%;
+  right: 0;
+  margin-top: -5px;
+  border-width: 5px 0 5px 5px;
+  border-left-color: #000;
+}
+.tooltip.bottom .tooltip-arrow {
+  top: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-left .tooltip-arrow {
+  top: 0;
+  right: 5px;
+  margin-top: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-right .tooltip-arrow {
+  top: 0;
+  left: 5px;
+  margin-top: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1060;
+  display: none;
+  max-width: 276px;
+  padding: 1px;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: left;
+  text-align: start;
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  letter-spacing: normal;
+  word-break: normal;
+  word-spacing: normal;
+  word-wrap: normal;
+  white-space: normal;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+
+  line-break: auto;
+}
+.popover.top {
+  margin-top: -10px;
+}
+.popover.right {
+  margin-left: 10px;
+}
+.popover.bottom {
+  margin-top: 10px;
+}
+.popover.left {
+  margin-left: -10px;
+}
+.popover-title {
+  padding: 8px 14px;
+  margin: 0;
+  font-size: 14px;
+  background-color: #f7f7f7;
+  border-bottom: 1px solid #ebebeb;
+  border-radius: 5px 5px 0 0;
+}
+.popover-content {
+  padding: 9px 14px;
+}
+.popover > .arrow,
+.popover > .arrow:after {
+  position: absolute;
+  display: block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.popover > .arrow {
+  border-width: 11px;
+}
+.popover > .arrow:after {
+  content: "";
+  border-width: 10px;
+}
+.popover.top > .arrow {
+  bottom: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-color: #999;
+  border-top-color: rgba(0, 0, 0, .25);
+  border-bottom-width: 0;
+}
+.popover.top > .arrow:after {
+  bottom: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-color: #fff;
+  border-bottom-width: 0;
+}
+.popover.right > .arrow {
+  top: 50%;
+  left: -11px;
+  margin-top: -11px;
+  border-right-color: #999;
+  border-right-color: rgba(0, 0, 0, .25);
+  border-left-width: 0;
+}
+.popover.right > .arrow:after {
+  bottom: -10px;
+  left: 1px;
+  content: " ";
+  border-right-color: #fff;
+  border-left-width: 0;
+}
+.popover.bottom > .arrow {
+  top: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-width: 0;
+  border-bottom-color: #999;
+  border-bottom-color: rgba(0, 0, 0, .25);
+}
+.popover.bottom > .arrow:after {
+  top: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-width: 0;
+  border-bottom-color: #fff;
+}
+.popover.left > .arrow {
+  top: 50%;
+  right: -11px;
+  margin-top: -11px;
+  border-right-width: 0;
+  border-left-color: #999;
+  border-left-color: rgba(0, 0, 0, .25);
+}
+.popover.left > .arrow:after {
+  right: 1px;
+  bottom: -10px;
+  content: " ";
+  border-right-width: 0;
+  border-left-color: #fff;
+}
+.carousel {
+  position: relative;
+}
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+.carousel-inner > .item {
+  position: relative;
+  display: none;
+  -webkit-transition: .6s ease-in-out left;
+       -o-transition: .6s ease-in-out left;
+          transition: .6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  line-height: 1;
+}
+@media all and (transform-3d), (-webkit-transform-3d) {
+  .carousel-inner > .item {
+    -webkit-transition: -webkit-transform .6s ease-in-out;
+         -o-transition:      -o-transform .6s ease-in-out;
+            transition:         transform .6s ease-in-out;
+
+    -webkit-backface-visibility: hidden;
+            backface-visibility: hidden;
+    -webkit-perspective: 1000px;
+            perspective: 1000px;
+  }
+  .carousel-inner > .item.next,
+  .carousel-inner > .item.active.right {
+    left: 0;
+    -webkit-transform: translate3d(100%, 0, 0);
+            transform: translate3d(100%, 0, 0);
+  }
+  .carousel-inner > .item.prev,
+  .carousel-inner > .item.active.left {
+    left: 0;
+    -webkit-transform: translate3d(-100%, 0, 0);
+            transform: translate3d(-100%, 0, 0);
+  }
+  .carousel-inner > .item.next.left,
+  .carousel-inner > .item.prev.right,
+  .carousel-inner > .item.active {
+    left: 0;
+    -webkit-transform: translate3d(0, 0, 0);
+            transform: translate3d(0, 0, 0);
+  }
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  display: block;
+}
+.carousel-inner > .active {
+  left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.carousel-inner > .next {
+  left: 100%;
+}
+.carousel-inner > .prev {
+  left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+  left: 0;
+}
+.carousel-inner > .active.left {
+  left: -100%;
+}
+.carousel-inner > .active.right {
+  left: 100%;
+}
+.carousel-control {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 15%;
+  font-size: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.carousel-control.left {
+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control.right {
+  right: 0;
+  left: auto;
+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+  color: #fff;
+  text-decoration: none;
+  filter: alpha(opacity=90);
+  outline: 0;
+  opacity: .9;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+  position: absolute;
+  top: 50%;
+  z-index: 5;
+  display: inline-block;
+  margin-top: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+  left: 50%;
+  margin-left: -10px;
+}
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+  right: 50%;
+  margin-right: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+  width: 20px;
+  height: 20px;
+  font-family: serif;
+  line-height: 1;
+}
+.carousel-control .icon-prev:before {
+  content: '\2039';
+}
+.carousel-control .icon-next:before {
+  content: '\203a';
+}
+.carousel-indicators {
+  position: absolute;
+  bottom: 10px;
+  left: 50%;
+  z-index: 15;
+  width: 60%;
+  padding-left: 0;
+  margin-left: -30%;
+  text-align: center;
+  list-style: none;
+}
+.carousel-indicators li {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  margin: 1px;
+  text-indent: -999px;
+  cursor: pointer;
+  background-color: #000 \9;
+  background-color: rgba(0, 0, 0, 0);
+  border: 1px solid #fff;
+  border-radius: 10px;
+}
+.carousel-indicators .active {
+  width: 12px;
+  height: 12px;
+  margin: 0;
+  background-color: #fff;
+}
+.carousel-caption {
+  position: absolute;
+  right: 15%;
+  bottom: 20px;
+  left: 15%;
+  z-index: 10;
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+}
+.carousel-caption .btn {
+  text-shadow: none;
+}
+@media screen and (min-width: 768px) {
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-prev,
+  .carousel-control .icon-next {
+    width: 30px;
+    height: 30px;
+    margin-top: -15px;
+    font-size: 30px;
+  }
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .icon-prev {
+    margin-left: -15px;
+  }
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-next {
+    margin-right: -15px;
+  }
+  .carousel-caption {
+    right: 20%;
+    left: 20%;
+    padding-bottom: 30px;
+  }
+  .carousel-indicators {
+    bottom: 20px;
+  }
+}
+.clearfix:before,
+.clearfix:after,
+.dl-horizontal dd:before,
+.dl-horizontal dd:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
+.modal-footer:before,
+.modal-footer:after {
+  display: table;
+  content: " ";
+}
+.clearfix:after,
+.dl-horizontal dd:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
+.modal-footer:after {
+  clear: both;
+}
+.center-block {
+  display: block;
+  margin-right: auto;
+  margin-left: auto;
+}
+.pull-right {
+  float: right !important;
+}
+.pull-left {
+  float: left !important;
+}
+.hide {
+  display: none !important;
+}
+.show {
+  display: block !important;
+}
+.invisible {
+  visibility: hidden;
+}
+.text-hide {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+.hidden {
+  display: none !important;
+}
+.affix {
+  position: fixed;
+}
+@-ms-viewport {
+  width: device-width;
+}
+.visible-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+  display: none !important;
+}
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-inline-block {
+  display: none !important;
+}
+@media (max-width: 767px) {
+  .visible-xs {
+    display: block !important;
+  }
+  table.visible-xs {
+    display: table !important;
+  }
+  tr.visible-xs {
+    display: table-row !important;
+  }
+  th.visible-xs,
+  td.visible-xs {
+    display: table-cell !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-block {
+    display: block !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-inline {
+    display: inline !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm {
+    display: block !important;
+  }
+  table.visible-sm {
+    display: table !important;
+  }
+  tr.visible-sm {
+    display: table-row !important;
+  }
+  th.visible-sm,
+  td.visible-sm {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-block {
+    display: block !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md {
+    display: block !important;
+  }
+  table.visible-md {
+    display: table !important;
+  }
+  tr.visible-md {
+    display: table-row !important;
+  }
+  th.visible-md,
+  td.visible-md {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-block {
+    display: block !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg {
+    display: block !important;
+  }
+  table.visible-lg {
+    display: table !important;
+  }
+  tr.visible-lg {
+    display: table-row !important;
+  }
+  th.visible-lg,
+  td.visible-lg {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-block {
+    display: block !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (max-width: 767px) {
+  .hidden-xs {
+    display: none !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .hidden-sm {
+    display: none !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .hidden-md {
+    display: none !important;
+  }
+}
+@media (min-width: 1200px) {
+  .hidden-lg {
+    display: none !important;
+  }
+}
+.visible-print {
+  display: none !important;
+}
+@media print {
+  .visible-print {
+    display: block !important;
+  }
+  table.visible-print {
+    display: table !important;
+  }
+  tr.visible-print {
+    display: table-row !important;
+  }
+  th.visible-print,
+  td.visible-print {
+    display: table-cell !important;
+  }
+}
+.visible-print-block {
+  display: none !important;
+}
+@media print {
+  .visible-print-block {
+    display: block !important;
+  }
+}
+.visible-print-inline {
+  display: none !important;
+}
+@media print {
+  .visible-print-inline {
+    display: inline !important;
+  }
+}
+.visible-print-inline-block {
+  display: none !important;
+}
+@media print {
+  .visible-print-inline-block {
+    display: inline-block !important;
+  }
+}
+@media print {
+  .hidden-print {
+    display: none !important;
+  }
+}
+/*# sourceMappingURL=bootstrap.css.map */
diff --git a/web/root/bootstrap/css/bootstrap.css.map b/web/root/bootstrap/css/bootstrap.css.map
new file mode 100644
index 0000000000..9f60ed2b1b
--- /dev/null
+++ b/web/root/bootstrap/css/bootstrap.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"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,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EA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DH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA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","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n  font-family: sans-serif;\n  -ms-text-size-adjust: 100%;\n  -webkit-text-size-adjust: 100%;\n}\nbody {\n  margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block;\n  vertical-align: baseline;\n}\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n[hidden],\ntemplate {\n  display: none;\n}\na {\n  background-color: transparent;\n}\na:active,\na:hover {\n  outline: 0;\n}\nabbr[title] {\n  border-bottom: 1px dotted;\n}\nb,\nstrong {\n  font-weight: bold;\n}\ndfn {\n  font-style: italic;\n}\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\nmark {\n  background: #ff0;\n  color: #000;\n}\nsmall {\n  font-size: 80%;\n}\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\nsup {\n  top: -0.5em;\n}\nsub {\n  bottom: -0.25em;\n}\nimg {\n  border: 0;\n}\nsvg:not(:root) {\n  overflow: hidden;\n}\nfigure {\n  margin: 1em 40px;\n}\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\npre {\n  overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit;\n  font: inherit;\n  margin: 0;\n}\nbutton {\n  overflow: visible;\n}\nbutton,\nselect {\n  text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button;\n  cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\ninput {\n  line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: textfield;\n  box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n  border: 0;\n  padding: 0;\n}\ntextarea {\n  overflow: auto;\n}\noptgroup {\n  font-weight: bold;\n}\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\ntd,\nth {\n  padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n  *,\n  *:before,\n  *:after {\n    background: transparent !important;\n    color: #000 !important;\n    box-shadow: none !important;\n    text-shadow: none !important;\n  }\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n  a[href^=\"#\"]:after,\n  a[href^=\"javascript:\"]:after {\n    content: \"\";\n  }\n  pre,\n  blockquote {\n    border: 1px solid #999;\n    page-break-inside: avoid;\n  }\n  thead {\n    display: table-header-group;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  img {\n    max-width: 100% !important;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  .navbar {\n    display: none;\n  }\n  .btn > .caret,\n  .dropup > .btn > .caret {\n    border-top-color: #000 !important;\n  }\n  .label {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #ddd !important;\n  }\n}\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('../fonts/glyphicons-halflings-regular.eot');\n  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n  content: \"\\2a\";\n}\n.glyphicon-plus:before {\n  content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n  content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n  content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n  content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n  content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n  content: \"\\270f\";\n}\n.glyphicon-glass:before {\n  content: \"\\e001\";\n}\n.glyphicon-music:before {\n  content: \"\\e002\";\n}\n.glyphicon-search:before {\n  content: \"\\e003\";\n}\n.glyphicon-heart:before {\n  content: \"\\e005\";\n}\n.glyphicon-star:before {\n  content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n  content: \"\\e007\";\n}\n.glyphicon-user:before {\n  content: \"\\e008\";\n}\n.glyphicon-film:before {\n  content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n  content: \"\\e010\";\n}\n.glyphicon-th:before {\n  content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n  content: \"\\e012\";\n}\n.glyphicon-ok:before {\n  content: \"\\e013\";\n}\n.glyphicon-remove:before {\n  content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n  content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n  content: \"\\e016\";\n}\n.glyphicon-off:before {\n  content: \"\\e017\";\n}\n.glyphicon-signal:before {\n  content: \"\\e018\";\n}\n.glyphicon-cog:before {\n  content: \"\\e019\";\n}\n.glyphicon-trash:before {\n  content: \"\\e020\";\n}\n.glyphicon-home:before {\n  content: \"\\e021\";\n}\n.glyphicon-file:before {\n  content: \"\\e022\";\n}\n.glyphicon-time:before {\n  content: \"\\e023\";\n}\n.glyphicon-road:before {\n  content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n  content: \"\\e025\";\n}\n.glyphicon-download:before {\n  content: \"\\e026\";\n}\n.glyphicon-upload:before {\n  content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n  content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n  content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n  content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n  content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n  content: \"\\e032\";\n}\n.glyphicon-lock:before {\n  content: \"\\e033\";\n}\n.glyphicon-flag:before {\n  content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n  content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n  content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n  content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n  content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n  content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n  content: \"\\e040\";\n}\n.glyphicon-tag:before {\n  content: \"\\e041\";\n}\n.glyphicon-tags:before {\n  content: \"\\e042\";\n}\n.glyphicon-book:before {\n  content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n  content: \"\\e044\";\n}\n.glyphicon-print:before {\n  content: \"\\e045\";\n}\n.glyphicon-camera:before {\n  content: \"\\e046\";\n}\n.glyphicon-font:before {\n  content: \"\\e047\";\n}\n.glyphicon-bold:before {\n  content: \"\\e048\";\n}\n.glyphicon-italic:before {\n  content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n  content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n  content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n  content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n  content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n  content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n  content: \"\\e055\";\n}\n.glyphicon-list:before {\n  content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n  content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n  content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n  content: \"\\e059\";\n}\n.glyphicon-picture:before {\n  content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n  content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n  content: \"\\e063\";\n}\n.glyphicon-tint:before {\n  content: \"\\e064\";\n}\n.glyphicon-edit:before {\n  content: \"\\e065\";\n}\n.glyphicon-share:before {\n  content: \"\\e066\";\n}\n.glyphicon-check:before {\n  content: \"\\e067\";\n}\n.glyphicon-move:before {\n  content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n  content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n  content: \"\\e070\";\n}\n.glyphicon-backward:before {\n  content: \"\\e071\";\n}\n.glyphicon-play:before {\n  content: \"\\e072\";\n}\n.glyphicon-pause:before {\n  content: \"\\e073\";\n}\n.glyphicon-stop:before {\n  content: \"\\e074\";\n}\n.glyphicon-forward:before {\n  content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n  content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n  content: \"\\e077\";\n}\n.glyphicon-eject:before {\n  content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n  content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n  content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n  content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n  content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n  content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n  content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n  content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n  content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n  content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n  content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n  content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n  content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n  content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n  content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n  content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n  content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n  content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n  content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n  content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n  content: \"\\e101\";\n}\n.glyphicon-gift:before {\n  content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n  content: \"\\e103\";\n}\n.glyphicon-fire:before {\n  content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n  content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n  content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n  content: \"\\e107\";\n}\n.glyphicon-plane:before {\n  content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n  content: \"\\e109\";\n}\n.glyphicon-random:before {\n  content: \"\\e110\";\n}\n.glyphicon-comment:before {\n  content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n  content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n  content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n  content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n  content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n  content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n  content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n  content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n  content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n  content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n  content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n  content: \"\\e122\";\n}\n.glyphicon-bell:before {\n  content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n  content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n  content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n  content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n  content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n  content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n  content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n  content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n  content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n  content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n  content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n  content: \"\\e134\";\n}\n.glyphicon-globe:before {\n  content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n  content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n  content: \"\\e137\";\n}\n.glyphicon-filter:before {\n  content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n  content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n  content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n  content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n  content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n  content: \"\\e143\";\n}\n.glyphicon-link:before {\n  content: \"\\e144\";\n}\n.glyphicon-phone:before {\n  content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n  content: \"\\e146\";\n}\n.glyphicon-usd:before {\n  content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n  content: \"\\e149\";\n}\n.glyphicon-sort:before {\n  content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n  content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n  content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n  content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n  content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n  content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n  content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n  content: \"\\e157\";\n}\n.glyphicon-expand:before {\n  content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n  content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n  content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n  content: \"\\e161\";\n}\n.glyphicon-flash:before {\n  content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n  content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n  content: \"\\e164\";\n}\n.glyphicon-record:before {\n  content: \"\\e165\";\n}\n.glyphicon-save:before {\n  content: \"\\e166\";\n}\n.glyphicon-open:before {\n  content: \"\\e167\";\n}\n.glyphicon-saved:before {\n  content: \"\\e168\";\n}\n.glyphicon-import:before {\n  content: \"\\e169\";\n}\n.glyphicon-export:before {\n  content: \"\\e170\";\n}\n.glyphicon-send:before {\n  content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n  content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n  content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n  content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n  content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n  content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n  content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n  content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n  content: \"\\e179\";\n}\n.glyphicon-header:before {\n  content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n  content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n  content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n  content: \"\\e183\";\n}\n.glyphicon-tower:before {\n  content: \"\\e184\";\n}\n.glyphicon-stats:before {\n  content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n  content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n  content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n  content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n  content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n  content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n  content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n  content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n  content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n  content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n  content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n  content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n  content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n  content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n  content: \"\\e200\";\n}\n.glyphicon-cd:before {\n  content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n  content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n  content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n  content: \"\\e204\";\n}\n.glyphicon-copy:before {\n  content: \"\\e205\";\n}\n.glyphicon-paste:before {\n  content: \"\\e206\";\n}\n.glyphicon-alert:before {\n  content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n  content: \"\\e210\";\n}\n.glyphicon-king:before {\n  content: \"\\e211\";\n}\n.glyphicon-queen:before {\n  content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n  content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n  content: \"\\e214\";\n}\n.glyphicon-knight:before {\n  content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n  content: \"\\e216\";\n}\n.glyphicon-tent:before {\n  content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n  content: \"\\e218\";\n}\n.glyphicon-bed:before {\n  content: \"\\e219\";\n}\n.glyphicon-apple:before {\n  content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n  content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n  content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n  content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n  content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n  content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n  content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n  content: \"\\e227\";\n}\n.glyphicon-btc:before {\n  content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n  content: \"\\e227\";\n}\n.glyphicon-yen:before {\n  content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n  content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n  content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n  content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n  content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n  content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n  content: \"\\e232\";\n}\n.glyphicon-education:before {\n  content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n  content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n  content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n  content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n  content: \"\\e237\";\n}\n.glyphicon-oil:before {\n  content: \"\\e238\";\n}\n.glyphicon-grain:before {\n  content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n  content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n  content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n  content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n  content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n  content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n  content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n  content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n  content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n  content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n  content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n  content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n  content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n  content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n  content: \"\\e253\";\n}\n.glyphicon-console:before {\n  content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n  content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n  content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n  content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n  content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n  content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n  content: \"\\e260\";\n}\n* {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\n*:before,\n*:after {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #333333;\n  background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\na {\n  color: #337ab7;\n  text-decoration: none;\n}\na:hover,\na:focus {\n  color: #23527c;\n  text-decoration: underline;\n}\na:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\nfigure {\n  margin: 0;\n}\nimg {\n  vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  max-width: 100%;\n  height: auto;\n}\n.img-rounded {\n  border-radius: 6px;\n}\n.img-thumbnail {\n  padding: 4px;\n  line-height: 1.42857143;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n  -o-transition: all 0.2s ease-in-out;\n  transition: all 0.2s ease-in-out;\n  display: inline-block;\n  max-width: 100%;\n  height: auto;\n}\n.img-circle {\n  border-radius: 50%;\n}\nhr {\n  margin-top: 20px;\n  margin-bottom: 20px;\n  border: 0;\n  border-top: 1px solid #eeeeee;\n}\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  clip: auto;\n}\n[role=\"button\"] {\n  cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n  font-weight: normal;\n  line-height: 1;\n  color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n  font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n  font-size: 75%;\n}\nh1,\n.h1 {\n  font-size: 36px;\n}\nh2,\n.h2 {\n  font-size: 30px;\n}\nh3,\n.h3 {\n  font-size: 24px;\n}\nh4,\n.h4 {\n  font-size: 18px;\n}\nh5,\n.h5 {\n  font-size: 14px;\n}\nh6,\n.h6 {\n  font-size: 12px;\n}\np {\n  margin: 0 0 10px;\n}\n.lead {\n  margin-bottom: 20px;\n  font-size: 16px;\n  font-weight: 300;\n  line-height: 1.4;\n}\n@media (min-width: 768px) {\n  .lead {\n    font-size: 21px;\n  }\n}\nsmall,\n.small {\n  font-size: 85%;\n}\nmark,\n.mark {\n  background-color: #fcf8e3;\n  padding: .2em;\n}\n.text-left {\n  text-align: left;\n}\n.text-right {\n  text-align: right;\n}\n.text-center {\n  text-align: center;\n}\n.text-justify {\n  text-align: justify;\n}\n.text-nowrap {\n  white-space: nowrap;\n}\n.text-lowercase {\n  text-transform: lowercase;\n}\n.text-uppercase {\n  text-transform: uppercase;\n}\n.text-capitalize {\n  text-transform: capitalize;\n}\n.text-muted {\n  color: #777777;\n}\n.text-primary {\n  color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n  color: #286090;\n}\n.text-success {\n  color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n  color: #2b542c;\n}\n.text-info {\n  color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n  color: #245269;\n}\n.text-warning {\n  color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n  color: #66512c;\n}\n.text-danger {\n  color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n  color: #843534;\n}\n.bg-primary {\n  color: #fff;\n  background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n  background-color: #286090;\n}\n.bg-success {\n  background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n  background-color: #c1e2b3;\n}\n.bg-info {\n  background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n  background-color: #afd9ee;\n}\n.bg-warning {\n  background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n  background-color: #f7ecb5;\n}\n.bg-danger {\n  background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n  background-color: #e4b9b9;\n}\n.page-header {\n  padding-bottom: 9px;\n  margin: 40px 0 20px;\n  border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n  margin-bottom: 0;\n}\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n  margin-left: -5px;\n}\n.list-inline > li {\n  display: inline-block;\n  padding-left: 5px;\n  padding-right: 5px;\n}\ndl {\n  margin-top: 0;\n  margin-bottom: 20px;\n}\ndt,\ndd {\n  line-height: 1.42857143;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0;\n}\n@media (min-width: 768px) {\n  .dl-horizontal dt {\n    float: left;\n    width: 160px;\n    clear: left;\n    text-align: right;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .dl-horizontal dd {\n    margin-left: 180px;\n  }\n}\nabbr[title],\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted #777777;\n}\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\nblockquote {\n  padding: 10px 20px;\n  margin: 0 0 20px;\n  font-size: 17.5px;\n  border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n  margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n  display: block;\n  font-size: 80%;\n  line-height: 1.42857143;\n  color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n  content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid #eeeeee;\n  border-left: 0;\n  text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n  content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n  content: '\\00A0 \\2014';\n}\naddress {\n  margin-bottom: 20px;\n  font-style: normal;\n  line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #c7254e;\n  background-color: #f9f2f4;\n  border-radius: 4px;\n}\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #ffffff;\n  background-color: #333333;\n  border-radius: 3px;\n  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: bold;\n  box-shadow: none;\n}\npre {\n  display: block;\n  padding: 9.5px;\n  margin: 0 0 10px;\n  font-size: 13px;\n  line-height: 1.42857143;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: #333333;\n  background-color: #f5f5f5;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\npre code {\n  padding: 0;\n  font-size: inherit;\n  color: inherit;\n  white-space: pre-wrap;\n  background-color: transparent;\n  border-radius: 0;\n}\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n.container {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n@media (min-width: 768px) {\n  .container {\n    width: 750px;\n  }\n}\n@media (min-width: 992px) {\n  .container {\n    width: 970px;\n  }\n}\n@media (min-width: 1200px) {\n  .container {\n    width: 1170px;\n  }\n}\n.container-fluid {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.row {\n  margin-left: -15px;\n  margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n  position: relative;\n  min-height: 1px;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n  float: left;\n}\n.col-xs-12 {\n  width: 100%;\n}\n.col-xs-11 {\n  width: 91.66666667%;\n}\n.col-xs-10 {\n  width: 83.33333333%;\n}\n.col-xs-9 {\n  width: 75%;\n}\n.col-xs-8 {\n  width: 66.66666667%;\n}\n.col-xs-7 {\n  width: 58.33333333%;\n}\n.col-xs-6 {\n  width: 50%;\n}\n.col-xs-5 {\n  width: 41.66666667%;\n}\n.col-xs-4 {\n  width: 33.33333333%;\n}\n.col-xs-3 {\n  width: 25%;\n}\n.col-xs-2 {\n  width: 16.66666667%;\n}\n.col-xs-1 {\n  width: 8.33333333%;\n}\n.col-xs-pull-12 {\n  right: 100%;\n}\n.col-xs-pull-11 {\n  right: 91.66666667%;\n}\n.col-xs-pull-10 {\n  right: 83.33333333%;\n}\n.col-xs-pull-9 {\n  right: 75%;\n}\n.col-xs-pull-8 {\n  right: 66.66666667%;\n}\n.col-xs-pull-7 {\n  right: 58.33333333%;\n}\n.col-xs-pull-6 {\n  right: 50%;\n}\n.col-xs-pull-5 {\n  right: 41.66666667%;\n}\n.col-xs-pull-4 {\n  right: 33.33333333%;\n}\n.col-xs-pull-3 {\n  right: 25%;\n}\n.col-xs-pull-2 {\n  right: 16.66666667%;\n}\n.col-xs-pull-1 {\n  right: 8.33333333%;\n}\n.col-xs-pull-0 {\n  right: auto;\n}\n.col-xs-push-12 {\n  left: 100%;\n}\n.col-xs-push-11 {\n  left: 91.66666667%;\n}\n.col-xs-push-10 {\n  left: 83.33333333%;\n}\n.col-xs-push-9 {\n  left: 75%;\n}\n.col-xs-push-8 {\n  left: 66.66666667%;\n}\n.col-xs-push-7 {\n  left: 58.33333333%;\n}\n.col-xs-push-6 {\n  left: 50%;\n}\n.col-xs-push-5 {\n  left: 41.66666667%;\n}\n.col-xs-push-4 {\n  left: 33.33333333%;\n}\n.col-xs-push-3 {\n  left: 25%;\n}\n.col-xs-push-2 {\n  left: 16.66666667%;\n}\n.col-xs-push-1 {\n  left: 8.33333333%;\n}\n.col-xs-push-0 {\n  left: auto;\n}\n.col-xs-offset-12 {\n  margin-left: 100%;\n}\n.col-xs-offset-11 {\n  margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n  margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n  margin-left: 75%;\n}\n.col-xs-offset-8 {\n  margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n  margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n  margin-left: 50%;\n}\n.col-xs-offset-5 {\n  margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n  margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n  margin-left: 25%;\n}\n.col-xs-offset-2 {\n  margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n  margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n  margin-left: 0%;\n}\n@media (min-width: 768px) {\n  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n    float: left;\n  }\n  .col-sm-12 {\n    width: 100%;\n  }\n  .col-sm-11 {\n    width: 91.66666667%;\n  }\n  .col-sm-10 {\n    width: 83.33333333%;\n  }\n  .col-sm-9 {\n    width: 75%;\n  }\n  .col-sm-8 {\n    width: 66.66666667%;\n  }\n  .col-sm-7 {\n    width: 58.33333333%;\n  }\n  .col-sm-6 {\n    width: 50%;\n  }\n  .col-sm-5 {\n    width: 41.66666667%;\n  }\n  .col-sm-4 {\n    width: 33.33333333%;\n  }\n  .col-sm-3 {\n    width: 25%;\n  }\n  .col-sm-2 {\n    width: 16.66666667%;\n  }\n  .col-sm-1 {\n    width: 8.33333333%;\n  }\n  .col-sm-pull-12 {\n    right: 100%;\n  }\n  .col-sm-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-sm-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-sm-pull-9 {\n    right: 75%;\n  }\n  .col-sm-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-sm-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-sm-pull-6 {\n    right: 50%;\n  }\n  .col-sm-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-sm-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-sm-pull-3 {\n    right: 25%;\n  }\n  .col-sm-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-sm-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-sm-pull-0 {\n    right: auto;\n  }\n  .col-sm-push-12 {\n    left: 100%;\n  }\n  .col-sm-push-11 {\n    left: 91.66666667%;\n  }\n  .col-sm-push-10 {\n    left: 83.33333333%;\n  }\n  .col-sm-push-9 {\n    left: 75%;\n  }\n  .col-sm-push-8 {\n    left: 66.66666667%;\n  }\n  .col-sm-push-7 {\n    left: 58.33333333%;\n  }\n  .col-sm-push-6 {\n    left: 50%;\n  }\n  .col-sm-push-5 {\n    left: 41.66666667%;\n  }\n  .col-sm-push-4 {\n    left: 33.33333333%;\n  }\n  .col-sm-push-3 {\n    left: 25%;\n  }\n  .col-sm-push-2 {\n    left: 16.66666667%;\n  }\n  .col-sm-push-1 {\n    left: 8.33333333%;\n  }\n  .col-sm-push-0 {\n    left: auto;\n  }\n  .col-sm-offset-12 {\n    margin-left: 100%;\n  }\n  .col-sm-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-sm-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-sm-offset-9 {\n    margin-left: 75%;\n  }\n  .col-sm-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-sm-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-sm-offset-6 {\n    margin-left: 50%;\n  }\n  .col-sm-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-sm-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-sm-offset-3 {\n    margin-left: 25%;\n  }\n  .col-sm-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-sm-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-sm-offset-0 {\n    margin-left: 0%;\n  }\n}\n@media (min-width: 992px) {\n  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n    float: left;\n  }\n  .col-md-12 {\n    width: 100%;\n  }\n  .col-md-11 {\n    width: 91.66666667%;\n  }\n  .col-md-10 {\n    width: 83.33333333%;\n  }\n  .col-md-9 {\n    width: 75%;\n  }\n  .col-md-8 {\n    width: 66.66666667%;\n  }\n  .col-md-7 {\n    width: 58.33333333%;\n  }\n  .col-md-6 {\n    width: 50%;\n  }\n  .col-md-5 {\n    width: 41.66666667%;\n  }\n  .col-md-4 {\n    width: 33.33333333%;\n  }\n  .col-md-3 {\n    width: 25%;\n  }\n  .col-md-2 {\n    width: 16.66666667%;\n  }\n  .col-md-1 {\n    width: 8.33333333%;\n  }\n  .col-md-pull-12 {\n    right: 100%;\n  }\n  .col-md-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-md-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-md-pull-9 {\n    right: 75%;\n  }\n  .col-md-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-md-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-md-pull-6 {\n    right: 50%;\n  }\n  .col-md-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-md-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-md-pull-3 {\n    right: 25%;\n  }\n  .col-md-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-md-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-md-pull-0 {\n    right: auto;\n  }\n  .col-md-push-12 {\n    left: 100%;\n  }\n  .col-md-push-11 {\n    left: 91.66666667%;\n  }\n  .col-md-push-10 {\n    left: 83.33333333%;\n  }\n  .col-md-push-9 {\n    left: 75%;\n  }\n  .col-md-push-8 {\n    left: 66.66666667%;\n  }\n  .col-md-push-7 {\n    left: 58.33333333%;\n  }\n  .col-md-push-6 {\n    left: 50%;\n  }\n  .col-md-push-5 {\n    left: 41.66666667%;\n  }\n  .col-md-push-4 {\n    left: 33.33333333%;\n  }\n  .col-md-push-3 {\n    left: 25%;\n  }\n  .col-md-push-2 {\n    left: 16.66666667%;\n  }\n  .col-md-push-1 {\n    left: 8.33333333%;\n  }\n  .col-md-push-0 {\n    left: auto;\n  }\n  .col-md-offset-12 {\n    margin-left: 100%;\n  }\n  .col-md-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-md-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-md-offset-9 {\n    margin-left: 75%;\n  }\n  .col-md-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-md-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-md-offset-6 {\n    margin-left: 50%;\n  }\n  .col-md-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-md-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-md-offset-3 {\n    margin-left: 25%;\n  }\n  .col-md-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-md-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-md-offset-0 {\n    margin-left: 0%;\n  }\n}\n@media (min-width: 1200px) {\n  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n    float: left;\n  }\n  .col-lg-12 {\n    width: 100%;\n  }\n  .col-lg-11 {\n    width: 91.66666667%;\n  }\n  .col-lg-10 {\n    width: 83.33333333%;\n  }\n  .col-lg-9 {\n    width: 75%;\n  }\n  .col-lg-8 {\n    width: 66.66666667%;\n  }\n  .col-lg-7 {\n    width: 58.33333333%;\n  }\n  .col-lg-6 {\n    width: 50%;\n  }\n  .col-lg-5 {\n    width: 41.66666667%;\n  }\n  .col-lg-4 {\n    width: 33.33333333%;\n  }\n  .col-lg-3 {\n    width: 25%;\n  }\n  .col-lg-2 {\n    width: 16.66666667%;\n  }\n  .col-lg-1 {\n    width: 8.33333333%;\n  }\n  .col-lg-pull-12 {\n    right: 100%;\n  }\n  .col-lg-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-lg-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-lg-pull-9 {\n    right: 75%;\n  }\n  .col-lg-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-lg-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-lg-pull-6 {\n    right: 50%;\n  }\n  .col-lg-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-lg-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-lg-pull-3 {\n    right: 25%;\n  }\n  .col-lg-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-lg-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-lg-pull-0 {\n    right: auto;\n  }\n  .col-lg-push-12 {\n    left: 100%;\n  }\n  .col-lg-push-11 {\n    left: 91.66666667%;\n  }\n  .col-lg-push-10 {\n    left: 83.33333333%;\n  }\n  .col-lg-push-9 {\n    left: 75%;\n  }\n  .col-lg-push-8 {\n    left: 66.66666667%;\n  }\n  .col-lg-push-7 {\n    left: 58.33333333%;\n  }\n  .col-lg-push-6 {\n    left: 50%;\n  }\n  .col-lg-push-5 {\n    left: 41.66666667%;\n  }\n  .col-lg-push-4 {\n    left: 33.33333333%;\n  }\n  .col-lg-push-3 {\n    left: 25%;\n  }\n  .col-lg-push-2 {\n    left: 16.66666667%;\n  }\n  .col-lg-push-1 {\n    left: 8.33333333%;\n  }\n  .col-lg-push-0 {\n    left: auto;\n  }\n  .col-lg-offset-12 {\n    margin-left: 100%;\n  }\n  .col-lg-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-lg-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-lg-offset-9 {\n    margin-left: 75%;\n  }\n  .col-lg-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-lg-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-lg-offset-6 {\n    margin-left: 50%;\n  }\n  .col-lg-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-lg-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-lg-offset-3 {\n    margin-left: 25%;\n  }\n  .col-lg-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-lg-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-lg-offset-0 {\n    margin-left: 0%;\n  }\n}\ntable {\n  background-color: transparent;\n}\ncaption {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  color: #777777;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.42857143;\n  vertical-align: top;\n  border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n  border-top: 0;\n}\n.table > tbody + tbody {\n  border-top: 2px solid #dddddd;\n}\n.table .table {\n  background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n  padding: 5px;\n}\n.table-bordered {\n  border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n  border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n  border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n  background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n  background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n  position: static;\n  float: none;\n  display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  position: static;\n  float: none;\n  display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n  background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n  background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n  background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n  background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n  background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n  background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n  background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n  background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n  background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n  background-color: #ebcccc;\n}\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: 15px;\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid #dddddd;\n  }\n  .table-responsive > .table {\n    margin-bottom: 0;\n  }\n  .table-responsive > .table > thead > tr > th,\n  .table-responsive > .table > tbody > tr > th,\n  .table-responsive > .table > tfoot > tr > th,\n  .table-responsive > .table > thead > tr > td,\n  .table-responsive > .table > tbody > tr > td,\n  .table-responsive > .table > tfoot > tr > td {\n    white-space: nowrap;\n  }\n  .table-responsive > .table-bordered {\n    border: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:first-child,\n  .table-responsive > .table-bordered > tbody > tr > th:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n  .table-responsive > .table-bordered > thead > tr > td:first-child,\n  .table-responsive > .table-bordered > tbody > tr > td:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n    border-left: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:last-child,\n  .table-responsive > .table-bordered > tbody > tr > th:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n  .table-responsive > .table-bordered > thead > tr > td:last-child,\n  .table-responsive > .table-bordered > tbody > tr > td:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n    border-right: 0;\n  }\n  .table-responsive > .table-bordered > tbody > tr:last-child > th,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n  .table-responsive > .table-bordered > tbody > tr:last-child > td,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n    border-bottom: 0;\n  }\n}\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  min-width: 0;\n}\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 20px;\n  font-size: 21px;\n  line-height: inherit;\n  color: #333333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n  display: inline-block;\n  max-width: 100%;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\ninput[type=\"search\"] {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9;\n  line-height: normal;\n}\ninput[type=\"file\"] {\n  display: block;\n}\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\nselect[multiple],\nselect[size] {\n  height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\noutput {\n  display: block;\n  padding-top: 7px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555555;\n}\n.form-control {\n  display: block;\n  width: 100%;\n  height: 34px;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555555;\n  background-color: #ffffff;\n  background-image: none;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n  border-color: #66afe9;\n  outline: 0;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n  color: #999999;\n  opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n  color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n  color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n  background-color: #eeeeee;\n  opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n  cursor: not-allowed;\n}\ntextarea.form-control {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"].form-control,\n  input[type=\"time\"].form-control,\n  input[type=\"datetime-local\"].form-control,\n  input[type=\"month\"].form-control {\n    line-height: 34px;\n  }\n  input[type=\"date\"].input-sm,\n  input[type=\"time\"].input-sm,\n  input[type=\"datetime-local\"].input-sm,\n  input[type=\"month\"].input-sm,\n  .input-group-sm input[type=\"date\"],\n  .input-group-sm input[type=\"time\"],\n  .input-group-sm input[type=\"datetime-local\"],\n  .input-group-sm input[type=\"month\"] {\n    line-height: 30px;\n  }\n  input[type=\"date\"].input-lg,\n  input[type=\"time\"].input-lg,\n  input[type=\"datetime-local\"].input-lg,\n  input[type=\"month\"].input-lg,\n  .input-group-lg input[type=\"date\"],\n  .input-group-lg input[type=\"time\"],\n  .input-group-lg input[type=\"datetime-local\"],\n  .input-group-lg input[type=\"month\"] {\n    line-height: 46px;\n  }\n}\n.form-group {\n  margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n  min-height: 20px;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n  cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n  cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n  cursor: not-allowed;\n}\n.form-control-static {\n  padding-top: 7px;\n  padding-bottom: 7px;\n  margin-bottom: 0;\n  min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n  padding-left: 0;\n  padding-right: 0;\n}\n.input-sm {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-sm {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n  height: auto;\n}\n.form-group-sm .form-control {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.form-group-sm select.form-control {\n  height: 30px;\n  line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n  height: auto;\n}\n.form-group-sm .form-control-static {\n  height: 30px;\n  min-height: 32px;\n  padding: 6px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n}\n.input-lg {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-lg {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n  height: auto;\n}\n.form-group-lg .form-control {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.form-group-lg select.form-control {\n  height: 46px;\n  line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n  height: auto;\n}\n.form-group-lg .form-control-static {\n  height: 46px;\n  min-height: 38px;\n  padding: 11px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n}\n.has-feedback {\n  position: relative;\n}\n.has-feedback .form-control {\n  padding-right: 42.5px;\n}\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  display: block;\n  width: 34px;\n  height: 34px;\n  line-height: 34px;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n  width: 46px;\n  height: 46px;\n  line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n  width: 30px;\n  height: 30px;\n  line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n  color: #3c763d;\n}\n.has-success .form-control {\n  border-color: #3c763d;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n  border-color: #2b542c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n  color: #3c763d;\n  border-color: #3c763d;\n  background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n  color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n  color: #8a6d3b;\n}\n.has-warning .form-control {\n  border-color: #8a6d3b;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n  border-color: #66512c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n  color: #8a6d3b;\n  border-color: #8a6d3b;\n  background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n  color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n  color: #a94442;\n}\n.has-error .form-control {\n  border-color: #a94442;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n  border-color: #843534;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n  color: #a94442;\n  border-color: #a94442;\n  background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n  color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n  top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n  top: 0;\n}\n.help-block {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: #737373;\n}\n@media (min-width: 768px) {\n  .form-inline .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-static {\n    display: inline-block;\n  }\n  .form-inline .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .form-inline .input-group .input-group-addon,\n  .form-inline .input-group .input-group-btn,\n  .form-inline .input-group .form-control {\n    width: auto;\n  }\n  .form-inline .input-group > .form-control {\n    width: 100%;\n  }\n  .form-inline .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio,\n  .form-inline .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio label,\n  .form-inline .checkbox label {\n    padding-left: 0;\n  }\n  .form-inline .radio input[type=\"radio\"],\n  .form-inline .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .form-inline .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n  margin-top: 0;\n  margin-bottom: 0;\n  padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n  min-height: 27px;\n}\n.form-horizontal .form-group {\n  margin-left: -15px;\n  margin-right: -15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .control-label {\n    text-align: right;\n    margin-bottom: 0;\n    padding-top: 7px;\n  }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n  right: 15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-lg .control-label {\n    padding-top: 14.333333px;\n    font-size: 18px;\n  }\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-sm .control-label {\n    padding-top: 6px;\n    font-size: 12px;\n  }\n}\n.btn {\n  display: inline-block;\n  margin-bottom: 0;\n  font-weight: normal;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none;\n  border: 1px solid transparent;\n  white-space: nowrap;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  border-radius: 4px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n  color: #333333;\n  text-decoration: none;\n}\n.btn:active,\n.btn.active {\n  outline: 0;\n  background-image: none;\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n  cursor: not-allowed;\n  opacity: 0.65;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n  pointer-events: none;\n}\n.btn-default {\n  color: #333333;\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n  color: #333333;\n  background-color: #e6e6e6;\n  border-color: #8c8c8c;\n}\n.btn-default:hover {\n  color: #333333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  color: #333333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n  color: #333333;\n  background-color: #d4d4d4;\n  border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n.btn-default .badge {\n  color: #ffffff;\n  background-color: #333333;\n}\n.btn-primary {\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n  color: #ffffff;\n  background-color: #286090;\n  border-color: #122b40;\n}\n.btn-primary:hover {\n  color: #ffffff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  color: #ffffff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n  color: #ffffff;\n  background-color: #204d74;\n  border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary .badge {\n  color: #337ab7;\n  background-color: #ffffff;\n}\n.btn-success {\n  color: #ffffff;\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n  color: #ffffff;\n  background-color: #449d44;\n  border-color: #255625;\n}\n.btn-success:hover {\n  color: #ffffff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  color: #ffffff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n  color: #ffffff;\n  background-color: #398439;\n  border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success .badge {\n  color: #5cb85c;\n  background-color: #ffffff;\n}\n.btn-info {\n  color: #ffffff;\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n  color: #ffffff;\n  background-color: #31b0d5;\n  border-color: #1b6d85;\n}\n.btn-info:hover {\n  color: #ffffff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  color: #ffffff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n  color: #ffffff;\n  background-color: #269abc;\n  border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info .badge {\n  color: #5bc0de;\n  background-color: #ffffff;\n}\n.btn-warning {\n  color: #ffffff;\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n  color: #ffffff;\n  background-color: #ec971f;\n  border-color: #985f0d;\n}\n.btn-warning:hover {\n  color: #ffffff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  color: #ffffff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n  color: #ffffff;\n  background-color: #d58512;\n  border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning .badge {\n  color: #f0ad4e;\n  background-color: #ffffff;\n}\n.btn-danger {\n  color: #ffffff;\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n  color: #ffffff;\n  background-color: #c9302c;\n  border-color: #761c19;\n}\n.btn-danger:hover {\n  color: #ffffff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  color: #ffffff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n  color: #ffffff;\n  background-color: #ac2925;\n  border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger .badge {\n  color: #d9534f;\n  background-color: #ffffff;\n}\n.btn-link {\n  color: #337ab7;\n  font-weight: normal;\n  border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n  background-color: transparent;\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n  border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n  color: #23527c;\n  text-decoration: underline;\n  background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n  color: #777777;\n  text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-block {\n  display: block;\n  width: 100%;\n}\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n  -o-transition: opacity 0.15s linear;\n  transition: opacity 0.15s linear;\n}\n.fade.in {\n  opacity: 1;\n}\n.collapse {\n  display: none;\n}\n.collapse.in {\n  display: block;\n}\ntr.collapse.in {\n  display: table-row;\n}\ntbody.collapse.in {\n  display: table-row-group;\n}\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition-property: height, visibility;\n  transition-property: height, visibility;\n  -webkit-transition-duration: 0.35s;\n  transition-duration: 0.35s;\n  -webkit-transition-timing-function: ease;\n  transition-timing-function: ease;\n}\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top: 4px dashed;\n  border-top: 4px solid \\9;\n  border-right: 4px solid transparent;\n  border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n  position: relative;\n}\n.dropdown-toggle:focus {\n  outline: 0;\n}\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0;\n  list-style: none;\n  font-size: 14px;\n  text-align: left;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n.dropdown-menu .divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n  display: block;\n  padding: 3px 20px;\n  clear: both;\n  font-weight: normal;\n  line-height: 1.42857143;\n  color: #333333;\n  white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  text-decoration: none;\n  color: #262626;\n  background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  color: #ffffff;\n  text-decoration: none;\n  outline: 0;\n  background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  text-decoration: none;\n  background-color: transparent;\n  background-image: none;\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  cursor: not-allowed;\n}\n.open > .dropdown-menu {\n  display: block;\n}\n.open > a {\n  outline: 0;\n}\n.dropdown-menu-right {\n  left: auto;\n  right: 0;\n}\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: 12px;\n  line-height: 1.42857143;\n  color: #777777;\n  white-space: nowrap;\n}\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: 990;\n}\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  border-top: 0;\n  border-bottom: 4px dashed;\n  border-bottom: 4px solid \\9;\n  content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n  .navbar-right .dropdown-menu {\n    left: auto;\n    right: 0;\n  }\n  .navbar-right .dropdown-menu-left {\n    left: 0;\n    right: auto;\n  }\n}\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n  z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n  margin-left: -1px;\n}\n.btn-toolbar {\n  margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n  float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n  margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n.btn-group > .btn:first-child {\n  margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn .caret {\n  margin-left: 0;\n}\n.btn-lg .caret {\n  border-width: 5px 5px 0;\n  border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n  border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n  display: block;\n  float: none;\n  width: 100%;\n  max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n  float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n  margin-top: -1px;\n  margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n  border-bottom-left-radius: 4px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n  float: none;\n  display: table-cell;\n  width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n  width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n  left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n.input-group {\n  position: relative;\n  display: table;\n  border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n  float: none;\n  padding-left: 0;\n  padding-right: 0;\n}\n.input-group .form-control {\n  position: relative;\n  z-index: 2;\n  float: left;\n  width: 100%;\n  margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n.input-group-addon {\n  padding: 6px 12px;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1;\n  color: #555555;\n  text-align: center;\n  background-color: #eeeeee;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\n.input-group-addon.input-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  border-radius: 3px;\n}\n.input-group-addon.input-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n.input-group-btn {\n  position: relative;\n  font-size: 0;\n  white-space: nowrap;\n}\n.input-group-btn > .btn {\n  position: relative;\n}\n.input-group-btn > .btn + .btn {\n  margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n  z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n  margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n  z-index: 2;\n  margin-left: -1px;\n}\n.nav {\n  margin-bottom: 0;\n  padding-left: 0;\n  list-style: none;\n}\n.nav > li {\n  position: relative;\n  display: block;\n}\n.nav > li > a {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n  color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n  color: #777777;\n  text-decoration: none;\n  background-color: transparent;\n  cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n  background-color: #eeeeee;\n  border-color: #337ab7;\n}\n.nav .nav-divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.nav > li > a > img {\n  max-width: none;\n}\n.nav-tabs {\n  border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n  float: left;\n  margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n  margin-right: 2px;\n  line-height: 1.42857143;\n  border: 1px solid transparent;\n  border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n  color: #555555;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-bottom-color: transparent;\n  cursor: default;\n}\n.nav-tabs.nav-justified {\n  width: 100%;\n  border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n  float: none;\n}\n.nav-tabs.nav-justified > li > a {\n  text-align: center;\n  margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-tabs.nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs.nav-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n  border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li > a {\n    border-bottom: 1px solid #dddddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs.nav-justified > .active > a,\n  .nav-tabs.nav-justified > .active > a:hover,\n  .nav-tabs.nav-justified > .active > a:focus {\n    border-bottom-color: #ffffff;\n  }\n}\n.nav-pills > li {\n  float: left;\n}\n.nav-pills > li > a {\n  border-radius: 4px;\n}\n.nav-pills > li + li {\n  margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n  color: #ffffff;\n  background-color: #337ab7;\n}\n.nav-stacked > li {\n  float: none;\n}\n.nav-stacked > li + li {\n  margin-top: 2px;\n  margin-left: 0;\n}\n.nav-justified {\n  width: 100%;\n}\n.nav-justified > li {\n  float: none;\n}\n.nav-justified > li > a {\n  text-align: center;\n  margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs-justified {\n  border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n  border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs-justified > li > a {\n    border-bottom: 1px solid #dddddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs-justified > .active > a,\n  .nav-tabs-justified > .active > a:hover,\n  .nav-tabs-justified > .active > a:focus {\n    border-bottom-color: #ffffff;\n  }\n}\n.tab-content > .tab-pane {\n  display: none;\n}\n.tab-content > .active {\n  display: block;\n}\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.navbar {\n  position: relative;\n  min-height: 50px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n  .navbar {\n    border-radius: 4px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-header {\n    float: left;\n  }\n}\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: 15px;\n  padding-left: 15px;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n  -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n  overflow-y: auto;\n}\n@media (min-width: 768px) {\n  .navbar-collapse {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n  }\n  .navbar-collapse.collapse {\n    display: block !important;\n    height: auto !important;\n    padding-bottom: 0;\n    overflow: visible !important;\n  }\n  .navbar-collapse.in {\n    overflow-y: visible;\n  }\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-static-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n  max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    max-height: 200px;\n  }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n@media (min-width: 768px) {\n  .container > .navbar-header,\n  .container-fluid > .navbar-header,\n  .container > .navbar-collapse,\n  .container-fluid > .navbar-collapse {\n    margin-right: 0;\n    margin-left: 0;\n  }\n}\n.navbar-static-top {\n  z-index: 1000;\n  border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n  .navbar-static-top {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n@media (min-width: 768px) {\n  .navbar-fixed-top,\n  .navbar-fixed-bottom {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0;\n  border-width: 1px 0 0;\n}\n.navbar-brand {\n  float: left;\n  padding: 15px 15px;\n  font-size: 18px;\n  line-height: 20px;\n  height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n  text-decoration: none;\n}\n.navbar-brand > img {\n  display: block;\n}\n@media (min-width: 768px) {\n  .navbar > .container .navbar-brand,\n  .navbar > .container-fluid .navbar-brand {\n    margin-left: -15px;\n  }\n}\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: 15px;\n  padding: 9px 10px;\n  margin-top: 8px;\n  margin-bottom: 8px;\n  background-color: transparent;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.navbar-toggle:focus {\n  outline: 0;\n}\n.navbar-toggle .icon-bar {\n  display: block;\n  width: 22px;\n  height: 2px;\n  border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n  margin-top: 4px;\n}\n@media (min-width: 768px) {\n  .navbar-toggle {\n    display: none;\n  }\n}\n.navbar-nav {\n  margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n  padding-top: 10px;\n  padding-bottom: 10px;\n  line-height: 20px;\n}\n@media (max-width: 767px) {\n  .navbar-nav .open .dropdown-menu {\n    position: static;\n    float: none;\n    width: auto;\n    margin-top: 0;\n    background-color: transparent;\n    border: 0;\n    box-shadow: none;\n  }\n  .navbar-nav .open .dropdown-menu > li > a,\n  .navbar-nav .open .dropdown-menu .dropdown-header {\n    padding: 5px 15px 5px 25px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a {\n    line-height: 20px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-nav .open .dropdown-menu > li > a:focus {\n    background-image: none;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-nav {\n    float: left;\n    margin: 0;\n  }\n  .navbar-nav > li {\n    float: left;\n  }\n  .navbar-nav > li > a {\n    padding-top: 15px;\n    padding-bottom: 15px;\n  }\n}\n.navbar-form {\n  margin-left: -15px;\n  margin-right: -15px;\n  padding: 10px 15px;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n  .navbar-form .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control-static {\n    display: inline-block;\n  }\n  .navbar-form .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .navbar-form .input-group .input-group-addon,\n  .navbar-form .input-group .input-group-btn,\n  .navbar-form .input-group .form-control {\n    width: auto;\n  }\n  .navbar-form .input-group > .form-control {\n    width: 100%;\n  }\n  .navbar-form .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio,\n  .navbar-form .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio label,\n  .navbar-form .checkbox label {\n    padding-left: 0;\n  }\n  .navbar-form .radio input[type=\"radio\"],\n  .navbar-form .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .navbar-form .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n@media (max-width: 767px) {\n  .navbar-form .form-group {\n    margin-bottom: 5px;\n  }\n  .navbar-form .form-group:last-child {\n    margin-bottom: 0;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-form {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    -webkit-box-shadow: none;\n    box-shadow: none;\n  }\n}\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.navbar-btn {\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n  margin-top: 14px;\n  margin-bottom: 14px;\n}\n.navbar-text {\n  margin-top: 15px;\n  margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n  .navbar-text {\n    float: left;\n    margin-left: 15px;\n    margin-right: 15px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-left {\n    float: left !important;\n  }\n  .navbar-right {\n    float: right !important;\n    margin-right: -15px;\n  }\n  .navbar-right ~ .navbar-right {\n    margin-right: 0;\n  }\n}\n.navbar-default {\n  background-color: #f8f8f8;\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n  color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n  color: #5e5e5e;\n  background-color: transparent;\n}\n.navbar-default .navbar-text {\n  color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n  color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n  color: #333333;\n  background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #555555;\n  background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n  color: #cccccc;\n  background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n  border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n  background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n  background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n  background-color: #e7e7e7;\n  color: #555555;\n}\n@media (max-width: 767px) {\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n    color: #777777;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #333333;\n    background-color: transparent;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #555555;\n    background-color: #e7e7e7;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #cccccc;\n    background-color: transparent;\n  }\n}\n.navbar-default .navbar-link {\n  color: #777777;\n}\n.navbar-default .navbar-link:hover {\n  color: #333333;\n}\n.navbar-default .btn-link {\n  color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n  color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n  color: #cccccc;\n}\n.navbar-inverse {\n  background-color: #222222;\n  border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n  color: #ffffff;\n  background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n  color: #444444;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n  border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n  background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n  background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n  border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n  background-color: #080808;\n  color: #ffffff;\n}\n@media (max-width: 767px) {\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n    border-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n    color: #9d9d9d;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #ffffff;\n    background-color: transparent;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #ffffff;\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #444444;\n    background-color: transparent;\n  }\n}\n.navbar-inverse .navbar-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n  color: #ffffff;\n}\n.navbar-inverse .btn-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n  color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n  color: #444444;\n}\n.breadcrumb {\n  padding: 8px 15px;\n  margin-bottom: 20px;\n  list-style: none;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n}\n.breadcrumb > li {\n  display: inline-block;\n}\n.breadcrumb > li + li:before {\n  content: \"/\\00a0\";\n  padding: 0 5px;\n  color: #cccccc;\n}\n.breadcrumb > .active {\n  color: #777777;\n}\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: 20px 0;\n  border-radius: 4px;\n}\n.pagination > li {\n  display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n  position: relative;\n  float: left;\n  padding: 6px 12px;\n  line-height: 1.42857143;\n  text-decoration: none;\n  color: #337ab7;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n  margin-left: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n  border-bottom-right-radius: 4px;\n  border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n  z-index: 3;\n  color: #23527c;\n  background-color: #eeeeee;\n  border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n  z-index: 2;\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n  cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n  color: #777777;\n  background-color: #ffffff;\n  border-color: #dddddd;\n  cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n  border-bottom-left-radius: 6px;\n  border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n  border-bottom-right-radius: 6px;\n  border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n  border-bottom-left-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n  border-bottom-right-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.pager {\n  padding-left: 0;\n  margin: 20px 0;\n  list-style: none;\n  text-align: center;\n}\n.pager li {\n  display: inline;\n}\n.pager li > a,\n.pager li > span {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n  float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n  float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n  color: #777777;\n  background-color: #ffffff;\n  cursor: not-allowed;\n}\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: #ffffff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.label:empty {\n  display: none;\n}\n.btn .label {\n  position: relative;\n  top: -1px;\n}\n.label-default {\n  background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n  background-color: #5e5e5e;\n}\n.label-primary {\n  background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n  background-color: #286090;\n}\n.label-success {\n  background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n  background-color: #449d44;\n}\n.label-info {\n  background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n  background-color: #31b0d5;\n}\n.label-warning {\n  background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n  background-color: #ec971f;\n}\n.label-danger {\n  background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n  background-color: #c9302c;\n}\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: 12px;\n  font-weight: bold;\n  color: #ffffff;\n  line-height: 1;\n  vertical-align: middle;\n  white-space: nowrap;\n  text-align: center;\n  background-color: #777777;\n  border-radius: 10px;\n}\n.badge:empty {\n  display: none;\n}\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n  top: 0;\n  padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: #337ab7;\n  background-color: #ffffff;\n}\n.list-group-item > .badge {\n  float: right;\n}\n.list-group-item > .badge + .badge {\n  margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n.jumbotron {\n  padding-top: 30px;\n  padding-bottom: 30px;\n  margin-bottom: 30px;\n  color: inherit;\n  background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n  color: inherit;\n}\n.jumbotron p {\n  margin-bottom: 15px;\n  font-size: 21px;\n  font-weight: 200;\n}\n.jumbotron > hr {\n  border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n  border-radius: 6px;\n}\n.jumbotron .container {\n  max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n  .jumbotron {\n    padding-top: 48px;\n    padding-bottom: 48px;\n  }\n  .container .jumbotron,\n  .container-fluid .jumbotron {\n    padding-left: 60px;\n    padding-right: 60px;\n  }\n  .jumbotron h1,\n  .jumbotron .h1 {\n    font-size: 63px;\n  }\n}\n.thumbnail {\n  display: block;\n  padding: 4px;\n  margin-bottom: 20px;\n  line-height: 1.42857143;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: border 0.2s ease-in-out;\n  -o-transition: border 0.2s ease-in-out;\n  transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n  margin-left: auto;\n  margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n  border-color: #337ab7;\n}\n.thumbnail .caption {\n  padding: 9px;\n  color: #333333;\n}\n.alert {\n  padding: 15px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.alert h4 {\n  margin-top: 0;\n  color: inherit;\n}\n.alert .alert-link {\n  font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n  margin-bottom: 0;\n}\n.alert > p + p {\n  margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n  padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  color: inherit;\n}\n.alert-success {\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n  color: #3c763d;\n}\n.alert-success hr {\n  border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n  color: #2b542c;\n}\n.alert-info {\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n  color: #31708f;\n}\n.alert-info hr {\n  border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n  color: #245269;\n}\n.alert-warning {\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n  color: #8a6d3b;\n}\n.alert-warning hr {\n  border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n  color: #66512c;\n}\n.alert-danger {\n  background-color: #f2dede;\n  border-color: #ebccd1;\n  color: #a94442;\n}\n.alert-danger hr {\n  border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n  color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n.progress {\n  overflow: hidden;\n  height: 20px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: 12px;\n  line-height: 20px;\n  color: #ffffff;\n  text-align: center;\n  background-color: #337ab7;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  -webkit-transition: width 0.6s ease;\n  -o-transition: width 0.6s ease;\n  transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n  -o-animation: progress-bar-stripes 2s linear infinite;\n  animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n  background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n  background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n  background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n  background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n  margin-top: 15px;\n}\n.media:first-child {\n  margin-top: 0;\n}\n.media,\n.media-body {\n  zoom: 1;\n  overflow: hidden;\n}\n.media-body {\n  width: 10000px;\n}\n.media-object {\n  display: block;\n}\n.media-object.img-thumbnail {\n  max-width: none;\n}\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n.media-middle {\n  vertical-align: middle;\n}\n.media-bottom {\n  vertical-align: bottom;\n}\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n.list-group {\n  margin-bottom: 20px;\n  padding-left: 0;\n}\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  margin-bottom: -1px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n  margin-bottom: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n  color: #555555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n  color: #333333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n  text-decoration: none;\n  color: #555555;\n  background-color: #f5f5f5;\n}\nbutton.list-group-item {\n  width: 100%;\n  text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n  background-color: #eeeeee;\n  color: #777777;\n  cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n  color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n  color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  z-index: 2;\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n  color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n  color: #c7ddef;\n}\n.list-group-item-success {\n  color: #3c763d;\n  background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n  color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n  color: #3c763d;\n  background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n  color: #fff;\n  background-color: #3c763d;\n  border-color: #3c763d;\n}\n.list-group-item-info {\n  color: #31708f;\n  background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n  color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n  color: #31708f;\n  background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n  color: #fff;\n  background-color: #31708f;\n  border-color: #31708f;\n}\n.list-group-item-warning {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n  color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n  color: #8a6d3b;\n  background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n  color: #fff;\n  background-color: #8a6d3b;\n  border-color: #8a6d3b;\n}\n.list-group-item-danger {\n  color: #a94442;\n  background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n  color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n  color: #a94442;\n  background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n  color: #fff;\n  background-color: #a94442;\n  border-color: #a94442;\n}\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n.panel {\n  margin-bottom: 20px;\n  background-color: #ffffff;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n  padding: 15px;\n}\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n  color: inherit;\n}\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: 16px;\n  color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n  color: inherit;\n}\n.panel-footer {\n  padding: 10px 15px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #dddddd;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n  margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n  border-width: 1px 0;\n  border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n  border-top: 0;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n  border-bottom: 0;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n  border-top-width: 0;\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n  margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n  border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n  border-bottom-left-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n  border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n  border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n  border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n  border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n  border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n  border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n  border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n  border-bottom: 0;\n}\n.panel > .table-responsive {\n  border: 0;\n  margin-bottom: 0;\n}\n.panel-group {\n  margin-bottom: 20px;\n}\n.panel-group .panel {\n  margin-bottom: 0;\n  border-radius: 4px;\n}\n.panel-group .panel + .panel {\n  margin-top: 5px;\n}\n.panel-group .panel-heading {\n  border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n  border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n  border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n  border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n  border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n  color: #333333;\n  background-color: #f5f5f5;\n  border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n  color: #f5f5f5;\n  background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #dddddd;\n}\n.panel-primary {\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n  color: #337ab7;\n  background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #337ab7;\n}\n.panel-success {\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n  color: #dff0d8;\n  background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #d6e9c6;\n}\n.panel-info {\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n  color: #d9edf7;\n  background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #bce8f1;\n}\n.panel-warning {\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n  color: #fcf8e3;\n  background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #faebcc;\n}\n.panel-danger {\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n  color: #f2dede;\n  background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  height: 100%;\n  width: 100%;\n  border: 0;\n}\n.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n  padding: 24px;\n  border-radius: 6px;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: 3px;\n}\n.close {\n  float: right;\n  font-size: 21px;\n  font-weight: bold;\n  line-height: 1;\n  color: #000000;\n  text-shadow: 0 1px 0 #ffffff;\n  opacity: 0.2;\n  filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n  color: #000000;\n  text-decoration: none;\n  cursor: pointer;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\nbutton.close {\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n  -webkit-appearance: none;\n}\n.modal-open {\n  overflow: hidden;\n}\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1050;\n  -webkit-overflow-scrolling: touch;\n  outline: 0;\n}\n.modal.fade .modal-dialog {\n  -webkit-transform: translate(0, -25%);\n  -ms-transform: translate(0, -25%);\n  -o-transform: translate(0, -25%);\n  transform: translate(0, -25%);\n  -webkit-transition: -webkit-transform 0.3s ease-out;\n  -moz-transition: -moz-transform 0.3s ease-out;\n  -o-transition: -o-transform 0.3s ease-out;\n  transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n  -webkit-transform: translate(0, 0);\n  -ms-transform: translate(0, 0);\n  -o-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n.modal-content {\n  position: relative;\n  background-color: #ffffff;\n  border: 1px solid #999999;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  background-clip: padding-box;\n  outline: 0;\n}\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1040;\n  background-color: #000000;\n}\n.modal-backdrop.fade {\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n.modal-header {\n  padding: 15px;\n  border-bottom: 1px solid #e5e5e5;\n  min-height: 16.42857143px;\n}\n.modal-header .close {\n  margin-top: -2px;\n}\n.modal-title {\n  margin: 0;\n  line-height: 1.42857143;\n}\n.modal-body {\n  position: relative;\n  padding: 15px;\n}\n.modal-footer {\n  padding: 15px;\n  text-align: right;\n  border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n  margin-left: 5px;\n  margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n  margin-left: 0;\n}\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n@media (min-width: 768px) {\n  .modal-dialog {\n    width: 600px;\n    margin: 30px auto;\n  }\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n  }\n  .modal-sm {\n    width: 300px;\n  }\n}\n@media (min-width: 992px) {\n  .modal-lg {\n    width: 900px;\n  }\n}\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-style: normal;\n  font-weight: normal;\n  letter-spacing: normal;\n  line-break: auto;\n  line-height: 1.42857143;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  white-space: normal;\n  word-break: normal;\n  word-spacing: normal;\n  word-wrap: normal;\n  font-size: 12px;\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n.tooltip.in {\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n.tooltip.top {\n  margin-top: -3px;\n  padding: 5px 0;\n}\n.tooltip.right {\n  margin-left: 3px;\n  padding: 0 5px;\n}\n.tooltip.bottom {\n  margin-top: 3px;\n  padding: 5px 0;\n}\n.tooltip.left {\n  margin-left: -3px;\n  padding: 0 5px;\n}\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #ffffff;\n  text-align: center;\n  background-color: #000000;\n  border-radius: 4px;\n}\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n  bottom: 0;\n  right: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n  bottom: 0;\n  left: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-width: 5px 5px 5px 0;\n  border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-width: 5px 0 5px 5px;\n  border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n  top: 0;\n  right: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n  top: 0;\n  left: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000000;\n}\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: none;\n  max-width: 276px;\n  padding: 1px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-style: normal;\n  font-weight: normal;\n  letter-spacing: normal;\n  line-break: auto;\n  line-height: 1.42857143;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  white-space: normal;\n  word-break: normal;\n  word-spacing: normal;\n  word-wrap: normal;\n  font-size: 14px;\n  background-color: #ffffff;\n  background-clip: padding-box;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n  margin-top: -10px;\n}\n.popover.right {\n  margin-left: 10px;\n}\n.popover.bottom {\n  margin-top: 10px;\n}\n.popover.left {\n  margin-left: -10px;\n}\n.popover-title {\n  margin: 0;\n  padding: 8px 14px;\n  font-size: 14px;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-radius: 5px 5px 0 0;\n}\n.popover-content {\n  padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.popover > .arrow {\n  border-width: 11px;\n}\n.popover > .arrow:after {\n  border-width: 10px;\n  content: \"\";\n}\n.popover.top > .arrow {\n  left: 50%;\n  margin-left: -11px;\n  border-bottom-width: 0;\n  border-top-color: #999999;\n  border-top-color: rgba(0, 0, 0, 0.25);\n  bottom: -11px;\n}\n.popover.top > .arrow:after {\n  content: \" \";\n  bottom: 1px;\n  margin-left: -10px;\n  border-bottom-width: 0;\n  border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n  top: 50%;\n  left: -11px;\n  margin-top: -11px;\n  border-left-width: 0;\n  border-right-color: #999999;\n  border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n  content: \" \";\n  left: 1px;\n  bottom: -10px;\n  border-left-width: 0;\n  border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n  left: 50%;\n  margin-left: -11px;\n  border-top-width: 0;\n  border-bottom-color: #999999;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n  top: -11px;\n}\n.popover.bottom > .arrow:after {\n  content: \" \";\n  top: 1px;\n  margin-left: -10px;\n  border-top-width: 0;\n  border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n  top: 50%;\n  right: -11px;\n  margin-top: -11px;\n  border-right-width: 0;\n  border-left-color: #999999;\n  border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n  content: \" \";\n  right: 1px;\n  border-right-width: 0;\n  border-left-color: #ffffff;\n  bottom: -10px;\n}\n.carousel {\n  position: relative;\n}\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n}\n.carousel-inner > .item {\n  display: none;\n  position: relative;\n  -webkit-transition: 0.6s ease-in-out left;\n  -o-transition: 0.6s ease-in-out left;\n  transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n  .carousel-inner > .item {\n    -webkit-transition: -webkit-transform 0.6s ease-in-out;\n    -moz-transition: -moz-transform 0.6s ease-in-out;\n    -o-transition: -o-transform 0.6s ease-in-out;\n    transition: transform 0.6s ease-in-out;\n    -webkit-backface-visibility: hidden;\n    -moz-backface-visibility: hidden;\n    backface-visibility: hidden;\n    -webkit-perspective: 1000px;\n    -moz-perspective: 1000px;\n    perspective: 1000px;\n  }\n  .carousel-inner > .item.next,\n  .carousel-inner > .item.active.right {\n    -webkit-transform: translate3d(100%, 0, 0);\n    transform: translate3d(100%, 0, 0);\n    left: 0;\n  }\n  .carousel-inner > .item.prev,\n  .carousel-inner > .item.active.left {\n    -webkit-transform: translate3d(-100%, 0, 0);\n    transform: translate3d(-100%, 0, 0);\n    left: 0;\n  }\n  .carousel-inner > .item.next.left,\n  .carousel-inner > .item.prev.right,\n  .carousel-inner > .item.active {\n    -webkit-transform: translate3d(0, 0, 0);\n    transform: translate3d(0, 0, 0);\n    left: 0;\n  }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n.carousel-inner > .active {\n  left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.carousel-inner > .next {\n  left: 100%;\n}\n.carousel-inner > .prev {\n  left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n.carousel-inner > .active.left {\n  left: -100%;\n}\n.carousel-inner > .active.right {\n  left: 100%;\n}\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: 15%;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n  font-size: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n  left: auto;\n  right: 0;\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n  outline: 0;\n  color: #ffffff;\n  text-decoration: none;\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  margin-top: -10px;\n  z-index: 5;\n  display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n  left: 50%;\n  margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n  right: 50%;\n  margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  line-height: 1;\n  font-family: serif;\n}\n.carousel-control .icon-prev:before {\n  content: '\\2039';\n}\n.carousel-control .icon-next:before {\n  content: '\\203a';\n}\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n}\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  border: 1px solid #ffffff;\n  border-radius: 10px;\n  cursor: pointer;\n  background-color: #000 \\9;\n  background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n  margin: 0;\n  width: 12px;\n  height: 12px;\n  background-color: #ffffff;\n}\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n  text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -15px;\n    font-size: 30px;\n  }\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .icon-prev {\n    margin-left: -15px;\n  }\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-next {\n    margin-right: -15px;\n  }\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n  content: \" \";\n  display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n  clear: both;\n}\n.center-block {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n.hidden {\n  display: none !important;\n}\n.affix {\n  position: fixed;\n}\n@-ms-viewport {\n  width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n@media (max-width: 767px) {\n  .visible-xs {\n    display: block !important;\n  }\n  table.visible-xs {\n    display: table !important;\n  }\n  tr.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-xs,\n  td.visible-xs {\n    display: table-cell !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-block {\n    display: block !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline {\n    display: inline !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm {\n    display: block !important;\n  }\n  table.visible-sm {\n    display: table !important;\n  }\n  tr.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-sm,\n  td.visible-sm {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-block {\n    display: block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md {\n    display: block !important;\n  }\n  table.visible-md {\n    display: table !important;\n  }\n  tr.visible-md {\n    display: table-row !important;\n  }\n  th.visible-md,\n  td.visible-md {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-block {\n    display: block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg {\n    display: block !important;\n  }\n  table.visible-lg {\n    display: table !important;\n  }\n  tr.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-lg,\n  td.visible-lg {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-block {\n    display: block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (max-width: 767px) {\n  .hidden-xs {\n    display: none !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-sm {\n    display: none !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-md {\n    display: none !important;\n  }\n}\n@media (min-width: 1200px) {\n  .hidden-lg {\n    display: none !important;\n  }\n}\n.visible-print {\n  display: none !important;\n}\n@media print {\n  .visible-print {\n    display: block !important;\n  }\n  table.visible-print {\n    display: table !important;\n  }\n  tr.visible-print {\n    display: table-row !important;\n  }\n  th.visible-print,\n  td.visible-print {\n    display: table-cell !important;\n  }\n}\n.visible-print-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-block {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline-block {\n    display: inline-block !important;\n  }\n}\n@media print {\n  .hidden-print {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n//    without disabling user zoom.\n//\n\nhtml {\n  font-family: sans-serif; // 1\n  -ms-text-size-adjust: 100%; // 2\n  -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n  margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; // 1\n  vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n  background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n  outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n  font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n  font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n  border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n  margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n  overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n//    Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; // 1\n  font: inherit; // 2\n  margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n  overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n//    and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n//    `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; // 2\n  cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n  line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; // 1\n  padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; // 1\n  box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n  border: 0; // 1\n  padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n  overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n  font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n    *,\n    *:before,\n    *:after {\n        background: transparent !important;\n        color: #000 !important; // Black prints faster: h5bp.com/s\n        box-shadow: none !important;\n        text-shadow: none !important;\n    }\n\n    a,\n    a:visited {\n        text-decoration: underline;\n    }\n\n    a[href]:after {\n        content: \" (\" attr(href) \")\";\n    }\n\n    abbr[title]:after {\n        content: \" (\" attr(title) \")\";\n    }\n\n    // Don't show links that are fragment identifiers,\n    // or use the `javascript:` pseudo protocol\n    a[href^=\"#\"]:after,\n    a[href^=\"javascript:\"]:after {\n        content: \"\";\n    }\n\n    pre,\n    blockquote {\n        border: 1px solid #999;\n        page-break-inside: avoid;\n    }\n\n    thead {\n        display: table-header-group; // h5bp.com/t\n    }\n\n    tr,\n    img {\n        page-break-inside: avoid;\n    }\n\n    img {\n        max-width: 100% !important;\n    }\n\n    p,\n    h2,\n    h3 {\n        orphans: 3;\n        widows: 3;\n    }\n\n    h2,\n    h3 {\n        page-break-after: avoid;\n    }\n\n    // Bootstrap specific changes start\n\n    // Bootstrap components\n    .navbar {\n        display: none;\n    }\n    .btn,\n    .dropup > .btn {\n        > .caret {\n            border-top-color: #000 !important;\n        }\n    }\n    .label {\n        border: 1px solid #000;\n    }\n\n    .table {\n        border-collapse: collapse !important;\n\n        td,\n        th {\n            background-color: #fff !important;\n        }\n    }\n    .table-bordered {\n        th,\n        td {\n            border: 1px solid #ddd !important;\n        }\n    }\n\n    // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// <a href=\"#\"><span class=\"glyphicon glyphicon-star\"></span> Star</a>\n\n// Import the fonts\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('@{icon-font-path}@{icon-font-name}.eot');\n  src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n       url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n       url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n       url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n       url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk               { &:before { content: \"\\2a\"; } }\n.glyphicon-plus                   { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur                    { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus                  { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud                  { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope               { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil                 { &:before { content: \"\\270f\"; } }\n.glyphicon-glass                  { &:before { content: \"\\e001\"; } }\n.glyphicon-music                  { &:before { content: \"\\e002\"; } }\n.glyphicon-search                 { &:before { content: \"\\e003\"; } }\n.glyphicon-heart                  { &:before { content: \"\\e005\"; } }\n.glyphicon-star                   { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty             { &:before { content: \"\\e007\"; } }\n.glyphicon-user                   { &:before { content: \"\\e008\"; } }\n.glyphicon-film                   { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large               { &:before { content: \"\\e010\"; } }\n.glyphicon-th                     { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list                { &:before { content: \"\\e012\"; } }\n.glyphicon-ok                     { &:before { content: \"\\e013\"; } }\n.glyphicon-remove                 { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in                { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out               { &:before { content: \"\\e016\"; } }\n.glyphicon-off                    { &:before { content: \"\\e017\"; } }\n.glyphicon-signal                 { &:before { content: \"\\e018\"; } }\n.glyphicon-cog                    { &:before { content: \"\\e019\"; } }\n.glyphicon-trash                  { &:before { content: \"\\e020\"; } }\n.glyphicon-home                   { &:before { content: \"\\e021\"; } }\n.glyphicon-file                   { &:before { content: \"\\e022\"; } }\n.glyphicon-time                   { &:before { content: \"\\e023\"; } }\n.glyphicon-road                   { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt           { &:before { content: \"\\e025\"; } }\n.glyphicon-download               { &:before { content: \"\\e026\"; } }\n.glyphicon-upload                 { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox                  { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle            { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat                 { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh                { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt               { &:before { content: \"\\e032\"; } }\n.glyphicon-lock                   { &:before { content: \"\\e033\"; } }\n.glyphicon-flag                   { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones             { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off             { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down            { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up              { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode                 { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode                { &:before { content: \"\\e040\"; } }\n.glyphicon-tag                    { &:before { content: \"\\e041\"; } }\n.glyphicon-tags                   { &:before { content: \"\\e042\"; } }\n.glyphicon-book                   { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark               { &:before { content: \"\\e044\"; } }\n.glyphicon-print                  { &:before { content: \"\\e045\"; } }\n.glyphicon-camera                 { &:before { content: \"\\e046\"; } }\n.glyphicon-font                   { &:before { content: \"\\e047\"; } }\n.glyphicon-bold                   { &:before { content: \"\\e048\"; } }\n.glyphicon-italic                 { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height            { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width             { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left             { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center           { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right            { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify          { &:before { content: \"\\e055\"; } }\n.glyphicon-list                   { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left            { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right           { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video         { &:before { content: \"\\e059\"; } }\n.glyphicon-picture                { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker             { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust                 { &:before { content: \"\\e063\"; } }\n.glyphicon-tint                   { &:before { content: \"\\e064\"; } }\n.glyphicon-edit                   { &:before { content: \"\\e065\"; } }\n.glyphicon-share                  { &:before { content: \"\\e066\"; } }\n.glyphicon-check                  { &:before { content: \"\\e067\"; } }\n.glyphicon-move                   { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward          { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward          { &:before { content: \"\\e070\"; } }\n.glyphicon-backward               { &:before { content: \"\\e071\"; } }\n.glyphicon-play                   { &:before { content: \"\\e072\"; } }\n.glyphicon-pause                  { &:before { content: \"\\e073\"; } }\n.glyphicon-stop                   { &:before { content: \"\\e074\"; } }\n.glyphicon-forward                { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward           { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward           { &:before { content: \"\\e077\"; } }\n.glyphicon-eject                  { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left           { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right          { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign              { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign             { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign            { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign                { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign          { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign              { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot             { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle          { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle              { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle             { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left             { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right            { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up               { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down             { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt              { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full            { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small           { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign       { &:before { content: \"\\e101\"; } }\n.glyphicon-gift                   { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf                   { &:before { content: \"\\e103\"; } }\n.glyphicon-fire                   { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open               { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close              { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign           { &:before { content: \"\\e107\"; } }\n.glyphicon-plane                  { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar               { &:before { content: \"\\e109\"; } }\n.glyphicon-random                 { &:before { content: \"\\e110\"; } }\n.glyphicon-comment                { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet                 { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up             { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down           { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet                { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart          { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close           { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open            { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical        { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal      { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd                    { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn               { &:before { content: \"\\e122\"; } }\n.glyphicon-bell                   { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate            { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up              { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down            { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right             { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left              { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up                { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down              { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right     { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left      { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up        { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down      { &:before { content: \"\\e134\"; } }\n.glyphicon-globe                  { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench                 { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks                  { &:before { content: \"\\e137\"; } }\n.glyphicon-filter                 { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase              { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen             { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard              { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip              { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty            { &:before { content: \"\\e143\"; } }\n.glyphicon-link                   { &:before { content: \"\\e144\"; } }\n.glyphicon-phone                  { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin                { &:before { content: \"\\e146\"; } }\n.glyphicon-usd                    { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp                    { &:before { content: \"\\e149\"; } }\n.glyphicon-sort                   { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet       { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt   { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order          { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt      { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes     { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked              { &:before { content: \"\\e157\"; } }\n.glyphicon-expand                 { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down          { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up            { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in                 { &:before { content: \"\\e161\"; } }\n.glyphicon-flash                  { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out                { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window             { &:before { content: \"\\e164\"; } }\n.glyphicon-record                 { &:before { content: \"\\e165\"; } }\n.glyphicon-save                   { &:before { content: \"\\e166\"; } }\n.glyphicon-open                   { &:before { content: \"\\e167\"; } }\n.glyphicon-saved                  { &:before { content: \"\\e168\"; } }\n.glyphicon-import                 { &:before { content: \"\\e169\"; } }\n.glyphicon-export                 { &:before { content: \"\\e170\"; } }\n.glyphicon-send                   { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk            { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved           { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove          { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save            { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open            { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card            { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer               { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery                { &:before { content: \"\\e179\"; } }\n.glyphicon-header                 { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed             { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone               { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt              { &:before { content: \"\\e183\"; } }\n.glyphicon-tower                  { &:before { content: \"\\e184\"; } }\n.glyphicon-stats                  { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video               { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video               { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles              { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo           { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby            { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1              { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1              { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1              { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark         { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark      { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download         { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload           { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer           { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous         { &:before { content: \"\\e200\"; } }\n.glyphicon-cd                     { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file              { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file              { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up               { &:before { content: \"\\e204\"; } }\n.glyphicon-copy                   { &:before { content: \"\\e205\"; } }\n.glyphicon-paste                  { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door                   { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key                    { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert                  { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer              { &:before { content: \"\\e210\"; } }\n.glyphicon-king                   { &:before { content: \"\\e211\"; } }\n.glyphicon-queen                  { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn                   { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop                 { &:before { content: \"\\e214\"; } }\n.glyphicon-knight                 { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula           { &:before { content: \"\\e216\"; } }\n.glyphicon-tent                   { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard             { &:before { content: \"\\e218\"; } }\n.glyphicon-bed                    { &:before { content: \"\\e219\"; } }\n.glyphicon-apple                  { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase                  { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass              { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp                   { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate              { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank             { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors               { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin                { &:before { content: \"\\e227\"; } }\n.glyphicon-btc                    { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt                    { &:before { content: \"\\e227\"; } }\n.glyphicon-yen                    { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy                    { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble                  { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub                    { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale                  { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly              { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted       { &:before { content: \"\\e232\"; } }\n.glyphicon-education              { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal      { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical        { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger         { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window           { &:before { content: \"\\e237\"; } }\n.glyphicon-oil                    { &:before { content: \"\\e238\"; } }\n.glyphicon-grain                  { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses             { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size              { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color             { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background        { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top       { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom    { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left      { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical  { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right     { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right         { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left          { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom        { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top           { &:before { content: \"\\e253\"; } }\n.glyphicon-console                { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript            { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript              { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left              { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right             { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down              { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up                { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n  .box-sizing(border-box);\n}\n*:before,\n*:after {\n  .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @text-color;\n  background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\n\n// Links\n\na {\n  color: @link-color;\n  text-decoration: none;\n\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n  }\n\n  &:focus {\n    .tab-focus();\n  }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n  margin: 0;\n}\n\n\n// Images\n\nimg {\n  vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n  .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n  border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n  padding: @thumbnail-padding;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(all .2s ease-in-out);\n\n  // Keep them at most 100% wide\n  .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n  border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n  margin-top:    @line-height-computed;\n  margin-bottom: @line-height-computed;\n  border: 0;\n  border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0,0,0,0);\n  border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n  &:active,\n  &:focus {\n    position: static;\n    width: auto;\n    height: auto;\n    margin: 0;\n    overflow: visible;\n    clip: auto;\n  }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n  cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n       -o-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n  -webkit-animation-fill-mode: @fill-mode;\n          animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  // Firefox\n  &::-moz-placeholder {\n    color: @color;\n    opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n  }\n  &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n  -webkit-transform: scale(@ratio);\n      -ms-transform: scale(@ratio); // IE9 only\n       -o-transform: scale(@ratio);\n          transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n  -webkit-transform: scale(@ratioX, @ratioY);\n      -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n       -o-transform: scale(@ratioX, @ratioY);\n          transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n  -webkit-transform: scaleX(@ratio);\n      -ms-transform: scaleX(@ratio); // IE9 only\n       -o-transform: scaleX(@ratio);\n          transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n  -webkit-transform: scaleY(@ratio);\n      -ms-transform: scaleY(@ratio); // IE9 only\n       -o-transform: scaleY(@ratio);\n          transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n  -webkit-transform: skewX(@x) skewY(@y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n       -o-transform: skewX(@x) skewY(@y);\n          transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n       -o-transform: translate(@x, @y);\n          transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n       -o-transform: rotate(@degrees);\n          transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n       -o-transform: rotateX(@degrees);\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n       -o-transform: rotateY(@degrees);\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n  -webkit-transition: @transition;\n       -o-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n  -webkit-transition-timing-function: @timing-function;\n          transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n  // Default\n  outline: thin dotted;\n  // WebKit\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n  display: @display;\n  max-width: 100%; // Part 1: Set a maximum relative to the parent\n  height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n  background-image: url(\"@{file-1x}\");\n\n  @media\n  only screen and (-webkit-min-device-pixel-ratio: 2),\n  only screen and (   min--moz-device-pixel-ratio: 2),\n  only screen and (     -o-min-device-pixel-ratio: 2/1),\n  only screen and (        min-device-pixel-ratio: 2),\n  only screen and (                min-resolution: 192dpi),\n  only screen and (                min-resolution: 2dppx) {\n    background-image: url(\"@{file-2x}\");\n    background-size: @width-1x @height-1x;\n  }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  font-family: @headings-font-family;\n  font-weight: @headings-font-weight;\n  line-height: @headings-line-height;\n  color: @headings-color;\n\n  small,\n  .small {\n    font-weight: normal;\n    line-height: 1;\n    color: @headings-small-color;\n  }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n  margin-top: @line-height-computed;\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 65%;\n  }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n  margin-top: (@line-height-computed / 2);\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 75%;\n  }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n  margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n  margin-bottom: @line-height-computed;\n  font-size: floor((@font-size-base * 1.15));\n  font-weight: 300;\n  line-height: 1.4;\n\n  @media (min-width: @screen-sm-min) {\n    font-size: (@font-size-base * 1.5);\n  }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n  font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n  background-color: @state-warning-bg;\n  padding: .2em;\n}\n\n// Alignment\n.text-left           { text-align: left; }\n.text-right          { text-align: right; }\n.text-center         { text-align: center; }\n.text-justify        { text-align: justify; }\n.text-nowrap         { white-space: nowrap; }\n\n// Transformation\n.text-lowercase      { text-transform: lowercase; }\n.text-uppercase      { text-transform: uppercase; }\n.text-capitalize     { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n  color: @text-muted;\n}\n.text-primary {\n  .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n  .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n  .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n  .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n  .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n  // Given the contrast here, this is the only class to have its color inverted\n  // automatically.\n  color: #fff;\n  .bg-variant(@brand-primary);\n}\n.bg-success {\n  .bg-variant(@state-success-bg);\n}\n.bg-info {\n  .bg-variant(@state-info-bg);\n}\n.bg-warning {\n  .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n  .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n  padding-bottom: ((@line-height-computed / 2) - 1);\n  margin: (@line-height-computed * 2) 0 @line-height-computed;\n  border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n  margin-top: 0;\n  margin-bottom: (@line-height-computed / 2);\n  ul,\n  ol {\n    margin-bottom: 0;\n  }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n  .list-unstyled();\n  margin-left: -5px;\n\n  > li {\n    display: inline-block;\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n// Description Lists\ndl {\n  margin-top: 0; // Remove browser default\n  margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n  line-height: @line-height-base;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n  dd {\n    &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    dt {\n      float: left;\n      width: (@dl-horizontal-offset - 20);\n      clear: left;\n      text-align: right;\n      .text-overflow();\n    }\n    dd {\n      margin-left: @dl-horizontal-offset;\n    }\n  }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n  font-size: 90%;\n  .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n  padding: (@line-height-computed / 2) @line-height-computed;\n  margin: 0 0 @line-height-computed;\n  font-size: @blockquote-font-size;\n  border-left: 5px solid @blockquote-border-color;\n\n  p,\n  ul,\n  ol {\n    &:last-child {\n      margin-bottom: 0;\n    }\n  }\n\n  // Note: Deprecated small and .small as of v3.1.0\n  // Context: https://github.com/twbs/bootstrap/issues/11660\n  footer,\n  small,\n  .small {\n    display: block;\n    font-size: 80%; // back to default font-size\n    line-height: @line-height-base;\n    color: @blockquote-small-color;\n\n    &:before {\n      content: '\\2014 \\00A0'; // em dash, nbsp\n    }\n  }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid @blockquote-border-color;\n  border-left: 0;\n  text-align: right;\n\n  // Account for citation\n  footer,\n  small,\n  .small {\n    &:before { content: ''; }\n    &:after {\n      content: '\\00A0 \\2014'; // nbsp, em dash\n    }\n  }\n}\n\n// Addresses\naddress {\n  margin-bottom: @line-height-computed;\n  font-style: normal;\n  line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n  color: @color;\n  a&:hover,\n  a&:focus {\n    color: darken(@color, 10%);\n  }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n  background-color: @color;\n  a&:hover,\n  a&:focus {\n    background-color: darken(@color, 10%);\n  }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n  font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @code-color;\n  background-color: @code-bg;\n  border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @kbd-color;\n  background-color: @kbd-bg;\n  border-radius: @border-radius-small;\n  box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n  kbd {\n    padding: 0;\n    font-size: 100%;\n    font-weight: bold;\n    box-shadow: none;\n  }\n}\n\n// Blocks of code\npre {\n  display: block;\n  padding: ((@line-height-computed - 1) / 2);\n  margin: 0 0 (@line-height-computed / 2);\n  font-size: (@font-size-base - 1); // 14px to 13px\n  line-height: @line-height-base;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: @pre-color;\n  background-color: @pre-bg;\n  border: 1px solid @pre-border-color;\n  border-radius: @border-radius-base;\n\n  // Account for some code outputs that place code tags in pre tags\n  code {\n    padding: 0;\n    font-size: inherit;\n    color: inherit;\n    white-space: pre-wrap;\n    background-color: transparent;\n    border-radius: 0;\n  }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n  max-height: @pre-scrollable-max-height;\n  overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n  .container-fixed();\n\n  @media (min-width: @screen-sm-min) {\n    width: @container-sm;\n  }\n  @media (min-width: @screen-md-min) {\n    width: @container-md;\n  }\n  @media (min-width: @screen-lg-min) {\n    width: @container-lg;\n  }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n  .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n  .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n  .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n  .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n  .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n  &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n  margin-left:  ceil((@gutter / -2));\n  margin-right: floor((@gutter / -2));\n  &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  float: left;\n  width: percentage((@columns / @grid-columns));\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n  margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n  left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n  right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-sm-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-offset(@columns) {\n  @media (min-width: @screen-sm-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-push(@columns) {\n  @media (min-width: @screen-sm-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-pull(@columns) {\n  @media (min-width: @screen-sm-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-md-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-offset(@columns) {\n  @media (min-width: @screen-md-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-push(@columns) {\n  @media (min-width: @screen-md-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-pull(@columns) {\n  @media (min-width: @screen-md-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-lg-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-offset(@columns) {\n  @media (min-width: @screen-lg-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-push(@columns) {\n  @media (min-width: @screen-lg-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-pull(@columns) {\n  @media (min-width: @screen-lg-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n  // Common styles for all sizes of grid columns, widths 1-12\n  .col(@index) { // initial\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      position: relative;\n      // Prevent columns from collapsing when empty\n      min-height: 1px;\n      // Inner gutter via padding\n      padding-left:  ceil((@grid-gutter-width / 2));\n      padding-right: floor((@grid-gutter-width / 2));\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n  .col(@index) { // initial\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      float: left;\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n  .col-@{class}-@{index} {\n    width: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n  .col-@{class}-push-@{index} {\n    left: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n  .col-@{class}-push-0 {\n    left: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n  .col-@{class}-pull-@{index} {\n    right: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n  .col-@{class}-pull-0 {\n    right: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n  .col-@{class}-offset-@{index} {\n    margin-left: percentage((@index / @grid-columns));\n  }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n  .calc-grid-column(@index, @class, @type);\n  // next iteration\n  .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n  .float-grid-columns(@class);\n  .loop-grid-columns(@grid-columns, @class, width);\n  .loop-grid-columns(@grid-columns, @class, pull);\n  .loop-grid-columns(@grid-columns, @class, push);\n  .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n  background-color: @table-bg;\n}\ncaption {\n  padding-top: @table-cell-padding;\n  padding-bottom: @table-cell-padding;\n  color: @text-muted;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: @line-height-computed;\n  // Cells\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-cell-padding;\n        line-height: @line-height-base;\n        vertical-align: top;\n        border-top: 1px solid @table-border-color;\n      }\n    }\n  }\n  // Bottom align for column headings\n  > thead > tr > th {\n    vertical-align: bottom;\n    border-bottom: 2px solid @table-border-color;\n  }\n  // Remove top border from thead by default\n  > caption + thead,\n  > colgroup + thead,\n  > thead:first-child {\n    > tr:first-child {\n      > th,\n      > td {\n        border-top: 0;\n      }\n    }\n  }\n  // Account for multiple tbody instances\n  > tbody + tbody {\n    border-top: 2px solid @table-border-color;\n  }\n\n  // Nesting\n  .table {\n    background-color: @body-bg;\n  }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-condensed-cell-padding;\n      }\n    }\n  }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n  border: 1px solid @table-border-color;\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        border: 1px solid @table-border-color;\n      }\n    }\n  }\n  > thead > tr {\n    > th,\n    > td {\n      border-bottom-width: 2px;\n    }\n  }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n  > tbody > tr:nth-of-type(odd) {\n    background-color: @table-bg-accent;\n  }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n  > tbody > tr:hover {\n    background-color: @table-bg-hover;\n  }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n  position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n  float: none;\n  display: table-column;\n}\ntable {\n  td,\n  th {\n    &[class*=\"col-\"] {\n      position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n      float: none;\n      display: table-cell;\n    }\n  }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n  @media screen and (max-width: @screen-xs-max) {\n    width: 100%;\n    margin-bottom: (@line-height-computed * 0.75);\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid @table-border-color;\n\n    // Tighten up spacing\n    > .table {\n      margin-bottom: 0;\n\n      // Ensure the content doesn't wrap\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th,\n          > td {\n            white-space: nowrap;\n          }\n        }\n      }\n    }\n\n    // Special overrides for the bordered tables\n    > .table-bordered {\n      border: 0;\n\n      // Nuke the appropriate borders so that the parent can handle them\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th:first-child,\n          > td:first-child {\n            border-left: 0;\n          }\n          > th:last-child,\n          > td:last-child {\n            border-right: 0;\n          }\n        }\n      }\n\n      // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n      // chances are there will be only one `tr` in a `thead` and that would\n      // remove the border altogether.\n      > tbody,\n      > tfoot {\n        > tr:last-child {\n          > th,\n          > td {\n            border-bottom: 0;\n          }\n        }\n      }\n\n    }\n  }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n  // Exact selectors below required to override `.table-striped` and prevent\n  // inheritance to nested tables.\n  .table > thead > tr,\n  .table > tbody > tr,\n  .table > tfoot > tr {\n    > td.@{state},\n    > th.@{state},\n    &.@{state} > td,\n    &.@{state} > th {\n      background-color: @background;\n    }\n  }\n\n  // Hover states for `.table-hover`\n  // Note: this is not available for cells or rows within `thead` or `tfoot`.\n  .table-hover > tbody > tr {\n    > td.@{state}:hover,\n    > th.@{state}:hover,\n    &.@{state}:hover > td,\n    &:hover > .@{state},\n    &.@{state}:hover > th {\n      background-color: darken(@background, 5%);\n    }\n  }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n  // so we reset that to ensure it behaves more like a standard block element.\n  // See https://github.com/twbs/bootstrap/issues/12359.\n  min-width: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: @line-height-computed;\n  font-size: (@font-size-base * 1.5);\n  line-height: inherit;\n  color: @legend-color;\n  border: 0;\n  border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n  display: inline-block;\n  max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n  .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9; // IE8-9\n  line-height: normal;\n}\n\ninput[type=\"file\"] {\n  display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  .tab-focus();\n}\n\n// Adjust output element\noutput {\n  display: block;\n  padding-top: (@padding-base-vertical + 1);\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n  background-color: @input-bg;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid @input-border;\n  border-radius: @input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS.\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n  .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n  // Customize the `:focus` state to imitate native WebKit styles.\n  .form-control-focus();\n\n  // Placeholder\n  .placeholder();\n\n  // Disabled and read-only inputs\n  //\n  // HTML5 says that controls under a fieldset > legend:first-child won't be\n  // disabled if the fieldset is disabled. Due to implementation difficulty, we\n  // don't honor that edge case; we style them as disabled anyway.\n  &[disabled],\n  &[readonly],\n  fieldset[disabled] & {\n    background-color: @input-bg-disabled;\n    opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n  }\n\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n\n  // Reset height for `textarea`s\n  textarea& {\n    height: auto;\n  }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"],\n  input[type=\"time\"],\n  input[type=\"datetime-local\"],\n  input[type=\"month\"] {\n    &.form-control {\n      line-height: @input-height-base;\n    }\n\n    &.input-sm,\n    .input-group-sm & {\n      line-height: @input-height-small;\n    }\n\n    &.input-lg,\n    .input-group-lg & {\n      line-height: @input-height-large;\n    }\n  }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n  margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n\n  label {\n    min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n    padding-left: 20px;\n    margin-bottom: 0;\n    font-weight: normal;\n    cursor: pointer;\n  }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because <label>s don't inherit their parent's `cursor`.\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  &[disabled],\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used directly on <label>s\n.radio-inline,\n.checkbox-inline {\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used on elements with <label> descendants\n.radio,\n.checkbox {\n  &.disabled,\n  fieldset[disabled] & {\n    label {\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n  // Size it appropriately next to real form controls\n  padding-top: (@padding-base-vertical + 1);\n  padding-bottom: (@padding-base-vertical + 1);\n  // Remove default margin from `p`\n  margin-bottom: 0;\n  min-height: (@line-height-computed + @font-size-base);\n\n  &.input-lg,\n  &.input-sm {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n//\n// The `.form-group-* form-control` variations are sadly duplicated to avoid the\n// issue documented in https://github.com/twbs/bootstrap/issues/15074.\n\n.input-sm {\n  .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);\n}\n.form-group-sm {\n  .form-control {\n    height: @input-height-small;\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    line-height: @line-height-small;\n    border-radius: @input-border-radius-small;\n  }\n  select.form-control {\n    height: @input-height-small;\n    line-height: @input-height-small;\n  }\n  textarea.form-control,\n  select[multiple].form-control {\n    height: auto;\n  }\n  .form-control-static {\n    height: @input-height-small;\n    min-height: (@line-height-computed + @font-size-small);\n    padding: (@padding-small-vertical + 1) @padding-small-horizontal;\n    font-size: @font-size-small;\n    line-height: @line-height-small;\n  }\n}\n\n.input-lg {\n  .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);\n}\n.form-group-lg {\n  .form-control {\n    height: @input-height-large;\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    line-height: @line-height-large;\n    border-radius: @input-border-radius-large;\n  }\n  select.form-control {\n    height: @input-height-large;\n    line-height: @input-height-large;\n  }\n  textarea.form-control,\n  select[multiple].form-control {\n    height: auto;\n  }\n  .form-control-static {\n    height: @input-height-large;\n    min-height: (@line-height-computed + @font-size-large);\n    padding: (@padding-large-vertical + 1) @padding-large-horizontal;\n    font-size: @font-size-large;\n    line-height: @line-height-large;\n  }\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n  // Enable absolute positioning\n  position: relative;\n\n  // Ensure icons don't overlap text\n  .form-control {\n    padding-right: (@input-height-base * 1.25);\n  }\n}\n// Feedback icon (requires .glyphicon classes)\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2; // Ensure icon is above input groups\n  display: block;\n  width: @input-height-base;\n  height: @input-height-base;\n  line-height: @input-height-base;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n  width: @input-height-large;\n  height: @input-height-large;\n  line-height: @input-height-large;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n  width: @input-height-small;\n  height: @input-height-small;\n  line-height: @input-height-small;\n}\n\n// Feedback states\n.has-success {\n  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n// Reposition feedback icon if input has visible label above\n.has-feedback label {\n\n  & ~ .form-control-feedback {\n     top: (@line-height-computed + 5); // Height of the `label` and its margin\n  }\n  &.sr-only ~ .form-control-feedback {\n     top: 0;\n  }\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n  display: block; // account for any element using help-block\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n  // Kick in the inline\n  @media (min-width: @screen-sm-min) {\n    // Inline-block all the things for \"inline\"\n    .form-group {\n      display: inline-block;\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // In navbar-form, allow folks to *not* use `.form-group`\n    .form-control {\n      display: inline-block;\n      width: auto; // Prevent labels from stacking above inputs in `.form-group`\n      vertical-align: middle;\n    }\n\n    // Make static controls behave like regular ones\n    .form-control-static {\n      display: inline-block;\n    }\n\n    .input-group {\n      display: inline-table;\n      vertical-align: middle;\n\n      .input-group-addon,\n      .input-group-btn,\n      .form-control {\n        width: auto;\n      }\n    }\n\n    // Input groups need that 100% width though\n    .input-group > .form-control {\n      width: 100%;\n    }\n\n    .control-label {\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // Remove default margin on radios/checkboxes that were used for stacking, and\n    // then undo the floating of radios and checkboxes to match.\n    .radio,\n    .checkbox {\n      display: inline-block;\n      margin-top: 0;\n      margin-bottom: 0;\n      vertical-align: middle;\n\n      label {\n        padding-left: 0;\n      }\n    }\n    .radio input[type=\"radio\"],\n    .checkbox input[type=\"checkbox\"] {\n      position: relative;\n      margin-left: 0;\n    }\n\n    // Re-override the feedback icon.\n    .has-feedback .form-control-feedback {\n      top: 0;\n    }\n  }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n  // Consistent vertical alignment of radios and checkboxes\n  //\n  // Labels also get some reset styles, but that is scoped to a media query below.\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline {\n    margin-top: 0;\n    margin-bottom: 0;\n    padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n  }\n  // Account for padding we're adding to ensure the alignment and of help text\n  // and other content below items\n  .radio,\n  .checkbox {\n    min-height: (@line-height-computed + (@padding-base-vertical + 1));\n  }\n\n  // Make form groups behave like rows\n  .form-group {\n    .make-row();\n  }\n\n  // Reset spacing and right align labels, but scope to media queries so that\n  // labels on narrow viewports stack the same as a default form example.\n  @media (min-width: @screen-sm-min) {\n    .control-label {\n      text-align: right;\n      margin-bottom: 0;\n      padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n    }\n  }\n\n  // Validation states\n  //\n  // Reposition the icon because it's now within a grid column and columns have\n  // `position: relative;` on them. Also accounts for the grid gutter padding.\n  .has-feedback .form-control-feedback {\n    right: floor((@grid-gutter-width / 2));\n  }\n\n  // Form group sizes\n  //\n  // Quick utility class for applying `.input-lg` and `.input-sm` styles to the\n  // inputs and labels within a `.form-group`.\n  .form-group-lg {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: ((@padding-large-vertical * @line-height-large) + 1);\n        font-size: @font-size-large;\n      }\n    }\n  }\n  .form-group-sm {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: (@padding-small-vertical + 1);\n        font-size: @font-size-small;\n      }\n    }\n  }\n}\n","// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n  // Color the label and help text\n  .help-block,\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline,\n  &.radio label,\n  &.checkbox label,\n  &.radio-inline label,\n  &.checkbox-inline label  {\n    color: @text-color;\n  }\n  // Set the border and box shadow on specific inputs to match\n  .form-control {\n    border-color: @border-color;\n    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n    &:focus {\n      border-color: darken(@border-color, 10%);\n      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n      .box-shadow(@shadow);\n    }\n  }\n  // Set validation states also for addons\n  .input-group-addon {\n    color: @text-color;\n    border-color: @border-color;\n    background-color: @background-color;\n  }\n  // Optional feedback icon\n  .form-control-feedback {\n    color: @text-color;\n  }\n}\n\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-border-focus` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n.form-control-focus(@color: @input-border-focus) {\n  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n  &:focus {\n    border-color: @color;\n    outline: 0;\n    .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n  }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. `<select>`\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  height: @input-height;\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n\n  select& {\n    height: @input-height;\n    line-height: @input-height;\n  }\n\n  textarea&,\n  select[multiple]& {\n    height: auto;\n  }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n  display: inline-block;\n  margin-bottom: 0; // For input.btn\n  font-weight: @btn-font-weight;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  white-space: nowrap;\n  .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n  .user-select(none);\n\n  &,\n  &:active,\n  &.active {\n    &:focus,\n    &.focus {\n      .tab-focus();\n    }\n  }\n\n  &:hover,\n  &:focus,\n  &.focus {\n    color: @btn-default-color;\n    text-decoration: none;\n  }\n\n  &:active,\n  &.active {\n    outline: 0;\n    background-image: none;\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n    .opacity(.65);\n    .box-shadow(none);\n  }\n\n  a& {\n    &.disabled,\n    fieldset[disabled] & {\n      pointer-events: none; // Future-proof disabling of clicks on `<a>` elements\n    }\n  }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n  .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n  .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n  .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n  .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n  .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n  .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n  color: @link-color;\n  font-weight: normal;\n  border-radius: 0;\n\n  &,\n  &:active,\n  &.active,\n  &[disabled],\n  fieldset[disabled] & {\n    background-color: transparent;\n    .box-shadow(none);\n  }\n  &,\n  &:hover,\n  &:focus,\n  &:active {\n    border-color: transparent;\n  }\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n    background-color: transparent;\n  }\n  &[disabled],\n  fieldset[disabled] & {\n    &:hover,\n    &:focus {\n      color: @btn-link-disabled-color;\n      text-decoration: none;\n    }\n  }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n  // line-height: ensure even-numbered height of button next to large input\n  .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n  // line-height: ensure proper height of button next to small input\n  .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n  .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n  &.btn-block {\n    width: 100%;\n  }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n  color: @color;\n  background-color: @background;\n  border-color: @border;\n\n  &:focus,\n  &.focus {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 25%);\n  }\n  &:hover {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 12%);\n  }\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 12%);\n\n    &:hover,\n    &:focus,\n    &.focus {\n      color: @color;\n      background-color: darken(@background, 17%);\n          border-color: darken(@border, 25%);\n    }\n  }\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    background-image: none;\n  }\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &.focus,\n    &:active,\n    &.active {\n      background-color: @background;\n          border-color: @border;\n    }\n  }\n\n  .badge {\n    color: @background;\n    background-color: @color;\n  }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n  opacity: @opacity;\n  // IE8 filter\n  @opacity-ie: (@opacity * 100);\n  filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n  opacity: 0;\n  .transition(opacity .15s linear);\n  &.in {\n    opacity: 1;\n  }\n}\n\n.collapse {\n  display: none;\n\n  &.in      { display: block; }\n  tr&.in    { display: table-row; }\n  tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  .transition-property(~\"height, visibility\");\n  .transition-duration(.35s);\n  .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top:   @caret-width-base dashed;\n  border-top:   @caret-width-base solid ~\"\\9\"; // IE8\n  border-right: @caret-width-base solid transparent;\n  border-left:  @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n  position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: @zindex-dropdown;\n  display: none; // none by default, but block on \"open\" of the menu\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0; // override default ul\n  list-style: none;\n  font-size: @font-size-base;\n  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n  background-color: @dropdown-bg;\n  border: 1px solid @dropdown-fallback-border; // IE8 fallback\n  border: 1px solid @dropdown-border;\n  border-radius: @border-radius-base;\n  .box-shadow(0 6px 12px rgba(0,0,0,.175));\n  background-clip: padding-box;\n\n  // Aligns the dropdown menu to right\n  //\n  // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n  &.pull-right {\n    right: 0;\n    left: auto;\n  }\n\n  // Dividers (basically an hr) within the dropdown\n  .divider {\n    .nav-divider(@dropdown-divider-bg);\n  }\n\n  // Links within the dropdown menu\n  > li > a {\n    display: block;\n    padding: 3px 20px;\n    clear: both;\n    font-weight: normal;\n    line-height: @line-height-base;\n    color: @dropdown-link-color;\n    white-space: nowrap; // prevent links from randomly breaking onto new lines\n  }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @dropdown-link-hover-color;\n    background-color: @dropdown-link-hover-bg;\n  }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-active-color;\n    text-decoration: none;\n    outline: 0;\n    background-color: @dropdown-link-active-bg;\n  }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-disabled-color;\n  }\n\n  // Nuke hover/focus effects\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    background-color: transparent;\n    background-image: none; // Remove CSS gradient\n    .reset-filter();\n    cursor: @cursor-disabled;\n  }\n}\n\n// Open state for the dropdown\n.open {\n  // Show the menu\n  > .dropdown-menu {\n    display: block;\n  }\n\n  // Remove the outline when :focus is triggered\n  > a {\n    outline: 0;\n  }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n  left: auto; // Reset the default from `.dropdown-menu`\n  right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: @font-size-small;\n  line-height: @line-height-base;\n  color: @dropdown-header-color;\n  white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n  // Reverse the caret\n  .caret {\n    border-top: 0;\n    border-bottom: @caret-width-base dashed;\n    border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n    content: \"\";\n  }\n  // Different positioning for bottom up menu\n  .dropdown-menu {\n    top: auto;\n    bottom: 100%;\n    margin-bottom: 2px;\n  }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-right {\n    .dropdown-menu {\n      .dropdown-menu-right();\n    }\n    // Necessary for overrides of the default right aligned menu.\n    // Will remove come v4 in all likelihood.\n    .dropdown-menu-left {\n      .dropdown-menu-left();\n    }\n  }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n  height: 1px;\n  margin: ((@line-height-computed / 2) - 1) 0;\n  overflow: hidden;\n  background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle; // match .btn alignment given font-size hack above\n  > .btn {\n    position: relative;\n    float: left;\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      z-index: 2;\n    }\n  }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n  .btn + .btn,\n  .btn + .btn-group,\n  .btn-group + .btn,\n  .btn-group + .btn-group {\n    margin-left: -1px;\n  }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n  margin-left: -5px; // Offset the first child's margin\n  &:extend(.clearfix all);\n\n  .btn,\n  .btn-group,\n  .input-group {\n    float: left;\n  }\n  > .btn,\n  > .btn-group,\n  > .input-group {\n    margin-left: 5px;\n  }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n  margin-left: 0;\n  &:not(:last-child):not(.dropdown-toggle) {\n    .border-right-radius(0);\n  }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-right-radius(0);\n  }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n  .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n  // Show no shadow for `.btn-link` since it has no other button styles.\n  &.btn-link {\n    .box-shadow(none);\n  }\n}\n\n\n// Reposition the caret\n.btn .caret {\n  margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n  border-width: @caret-width-large @caret-width-large 0;\n  border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n  border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n  > .btn,\n  > .btn-group,\n  > .btn-group > .btn {\n    display: block;\n    float: none;\n    width: 100%;\n    max-width: 100%;\n  }\n\n  // Clear floats so dropdown menus can be properly placed\n  > .btn-group {\n    &:extend(.clearfix all);\n    > .btn {\n      float: none;\n    }\n  }\n\n  > .btn + .btn,\n  > .btn + .btn-group,\n  > .btn-group + .btn,\n  > .btn-group + .btn-group {\n    margin-top: -1px;\n    margin-left: 0;\n  }\n}\n\n.btn-group-vertical > .btn {\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n  &:first-child:not(:last-child) {\n    border-top-right-radius: @btn-border-radius-base;\n    .border-bottom-radius(0);\n  }\n  &:last-child:not(:first-child) {\n    border-bottom-left-radius: @btn-border-radius-base;\n    .border-top-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-bottom-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n  > .btn,\n  > .btn-group {\n    float: none;\n    display: table-cell;\n    width: 1%;\n  }\n  > .btn-group .btn {\n    width: 100%;\n  }\n\n  > .btn-group .dropdown-menu {\n    left: auto;\n  }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n  > .btn,\n  > .btn-group > .btn {\n    input[type=\"radio\"],\n    input[type=\"checkbox\"] {\n      position: absolute;\n      clip: rect(0,0,0,0);\n      pointer-events: none;\n    }\n  }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n  border-top-right-radius: @radius;\n   border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n  border-bottom-right-radius: @radius;\n     border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n  border-bottom-right-radius: @radius;\n   border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n  border-bottom-left-radius: @radius;\n     border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n  position: relative; // For dropdowns\n  display: table;\n  border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n  // Undo padding and float of grid classes\n  &[class*=\"col-\"] {\n    float: none;\n    padding-left: 0;\n    padding-right: 0;\n  }\n\n  .form-control {\n    // Ensure that the input is always above the *appended* addon button for\n    // proper border colors.\n    position: relative;\n    z-index: 2;\n\n    // IE9 fubars the placeholder attribute in text inputs and the arrows on\n    // select elements in input groups. To fix it, we float the input. Details:\n    // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n    float: left;\n\n    width: 100%;\n    margin-bottom: 0;\n  }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: 1;\n  color: @input-color;\n  text-align: center;\n  background-color: @input-group-addon-bg;\n  border: 1px solid @input-group-addon-border-color;\n  border-radius: @border-radius-base;\n\n  // Sizing\n  &.input-sm {\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    border-radius: @border-radius-small;\n  }\n  &.input-lg {\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    border-radius: @border-radius-large;\n  }\n\n  // Nuke default margins from checkboxes and radios to vertically center within.\n  input[type=\"radio\"],\n  input[type=\"checkbox\"] {\n    margin-top: 0;\n  }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  .border-right-radius(0);\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  .border-left-radius(0);\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n  position: relative;\n  // Jankily prevent input button groups from wrapping with `white-space` and\n  // `font-size` in combination with `inline-block` on buttons.\n  font-size: 0;\n  white-space: nowrap;\n\n  // Negative margin for spacing, position for bringing hovered/focused/actived\n  // element above the siblings.\n  > .btn {\n    position: relative;\n    + .btn {\n      margin-left: -1px;\n    }\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active {\n      z-index: 2;\n    }\n  }\n\n  // Negative margin to only have a 1px border between the two\n  &:first-child {\n    > .btn,\n    > .btn-group {\n      margin-right: -1px;\n    }\n  }\n  &:last-child {\n    > .btn,\n    > .btn-group {\n      z-index: 2;\n      margin-left: -1px;\n    }\n  }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n  margin-bottom: 0;\n  padding-left: 0; // Override default ul/ol\n  list-style: none;\n  &:extend(.clearfix all);\n\n  > li {\n    position: relative;\n    display: block;\n\n    > a {\n      position: relative;\n      display: block;\n      padding: @nav-link-padding;\n      &:hover,\n      &:focus {\n        text-decoration: none;\n        background-color: @nav-link-hover-bg;\n      }\n    }\n\n    // Disabled state sets text to gray and nukes hover/tab effects\n    &.disabled > a {\n      color: @nav-disabled-link-color;\n\n      &:hover,\n      &:focus {\n        color: @nav-disabled-link-hover-color;\n        text-decoration: none;\n        background-color: transparent;\n        cursor: @cursor-disabled;\n      }\n    }\n  }\n\n  // Open dropdowns\n  .open > a {\n    &,\n    &:hover,\n    &:focus {\n      background-color: @nav-link-hover-bg;\n      border-color: @link-color;\n    }\n  }\n\n  // Nav dividers (deprecated with v3.0.1)\n  //\n  // This should have been removed in v3 with the dropping of `.nav-list`, but\n  // we missed it. We don't currently support this anywhere, but in the interest\n  // of maintaining backward compatibility in case you use it, it's deprecated.\n  .nav-divider {\n    .nav-divider();\n  }\n\n  // Prevent IE8 from misplacing imgs\n  //\n  // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n  > li > a > img {\n    max-width: none;\n  }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n  border-bottom: 1px solid @nav-tabs-border-color;\n  > li {\n    float: left;\n    // Make the list-items overlay the bottom border\n    margin-bottom: -1px;\n\n    // Actual tabs (as links)\n    > a {\n      margin-right: 2px;\n      line-height: @line-height-base;\n      border: 1px solid transparent;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n      &:hover {\n        border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n      }\n    }\n\n    // Active state, and its :hover to override normal :hover\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-tabs-active-link-hover-color;\n        background-color: @nav-tabs-active-link-hover-bg;\n        border: 1px solid @nav-tabs-active-link-hover-border-color;\n        border-bottom-color: transparent;\n        cursor: default;\n      }\n    }\n  }\n  // pulling this in mainly for less shorthand\n  &.nav-justified {\n    .nav-justified();\n    .nav-tabs-justified();\n  }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n  > li {\n    float: left;\n\n    // Links rendered as pills\n    > a {\n      border-radius: @nav-pills-border-radius;\n    }\n    + li {\n      margin-left: 2px;\n    }\n\n    // Active state\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-pills-active-link-hover-color;\n        background-color: @nav-pills-active-link-hover-bg;\n      }\n    }\n  }\n}\n\n\n// Stacked pills\n.nav-stacked {\n  > li {\n    float: none;\n    + li {\n      margin-top: 2px;\n      margin-left: 0; // no need for this gap between nav items\n    }\n  }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n  width: 100%;\n\n  > li {\n    float: none;\n    > a {\n      text-align: center;\n      margin-bottom: 5px;\n    }\n  }\n\n  > .dropdown .dropdown-menu {\n    top: auto;\n    left: auto;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li {\n      display: table-cell;\n      width: 1%;\n      > a {\n        margin-bottom: 0;\n      }\n    }\n  }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n  border-bottom: 0;\n\n  > li > a {\n    // Override margin from .nav-tabs\n    margin-right: 0;\n    border-radius: @border-radius-base;\n  }\n\n  > .active > a,\n  > .active > a:hover,\n  > .active > a:focus {\n    border: 1px solid @nav-tabs-justified-link-border-color;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li > a {\n      border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n    }\n    > .active > a,\n    > .active > a:hover,\n    > .active > a:focus {\n      border-bottom-color: @nav-tabs-justified-active-link-border-color;\n    }\n  }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n  > .tab-pane {\n    display: none;\n  }\n  > .active {\n    display: block;\n  }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n  // make dropdown border overlap tab border\n  margin-top: -1px;\n  // Remove the top rounded corners here since there is a hard edge above the menu\n  .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n  position: relative;\n  min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n  margin-bottom: @navbar-margin-bottom;\n  border: 1px solid transparent;\n\n  // Prevent floats from breaking the navbar\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: @navbar-border-radius;\n  }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n  }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: @navbar-padding-horizontal;\n  padding-left:  @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n  &:extend(.clearfix all);\n  -webkit-overflow-scrolling: touch;\n\n  &.in {\n    overflow-y: auto;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n\n    &.collapse {\n      display: block !important;\n      height: auto !important;\n      padding-bottom: 0; // Override default setting\n      overflow: visible !important;\n    }\n\n    &.in {\n      overflow-y: visible;\n    }\n\n    // Undo the collapse side padding for navbars with containers to ensure\n    // alignment of right-aligned contents.\n    .navbar-fixed-top &,\n    .navbar-static-top &,\n    .navbar-fixed-bottom & {\n      padding-left: 0;\n      padding-right: 0;\n    }\n  }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  .navbar-collapse {\n    max-height: @navbar-collapse-max-height;\n\n    @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n      max-height: 200px;\n    }\n  }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n  > .navbar-header,\n  > .navbar-collapse {\n    margin-right: -@navbar-padding-horizontal;\n    margin-left:  -@navbar-padding-horizontal;\n\n    @media (min-width: @grid-float-breakpoint) {\n      margin-right: 0;\n      margin-left:  0;\n    }\n  }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n  z-index: @zindex-navbar;\n  border-width: 0 0 1px;\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: @zindex-navbar-fixed;\n\n  // Undo the rounded corners\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0; // override .navbar defaults\n  border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n  float: left;\n  padding: @navbar-padding-vertical @navbar-padding-horizontal;\n  font-size: @font-size-large;\n  line-height: @line-height-computed;\n  height: @navbar-height;\n\n  &:hover,\n  &:focus {\n    text-decoration: none;\n  }\n\n  > img {\n    display: block;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    .navbar > .container &,\n    .navbar > .container-fluid & {\n      margin-left: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: @navbar-padding-horizontal;\n  padding: 9px 10px;\n  .navbar-vertical-align(34px);\n  background-color: transparent;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  border-radius: @border-radius-base;\n\n  // We remove the `outline` here, but later compensate by attaching `:hover`\n  // styles to `:focus`.\n  &:focus {\n    outline: 0;\n  }\n\n  // Bars\n  .icon-bar {\n    display: block;\n    width: 22px;\n    height: 2px;\n    border-radius: 1px;\n  }\n  .icon-bar + .icon-bar {\n    margin-top: 4px;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    display: none;\n  }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n  margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n  > li > a {\n    padding-top:    10px;\n    padding-bottom: 10px;\n    line-height: @line-height-computed;\n  }\n\n  @media (max-width: @grid-float-breakpoint-max) {\n    // Dropdowns get custom display when collapsed\n    .open .dropdown-menu {\n      position: static;\n      float: none;\n      width: auto;\n      margin-top: 0;\n      background-color: transparent;\n      border: 0;\n      box-shadow: none;\n      > li > a,\n      .dropdown-header {\n        padding: 5px 15px 5px 25px;\n      }\n      > li > a {\n        line-height: @line-height-computed;\n        &:hover,\n        &:focus {\n          background-image: none;\n        }\n      }\n    }\n  }\n\n  // Uncollapse the nav\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin: 0;\n\n    > li {\n      float: left;\n      > a {\n        padding-top:    @navbar-padding-vertical;\n        padding-bottom: @navbar-padding-vertical;\n      }\n    }\n  }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n  margin-left: -@navbar-padding-horizontal;\n  margin-right: -@navbar-padding-horizontal;\n  padding: 10px @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n\n  // Mixin behavior for optimum display\n  .form-inline();\n\n  .form-group {\n    @media (max-width: @grid-float-breakpoint-max) {\n      margin-bottom: 5px;\n\n      &:last-child {\n        margin-bottom: 0;\n      }\n    }\n  }\n\n  // Vertically center in expanded, horizontal navbar\n  .navbar-vertical-align(@input-height-base);\n\n  // Undo 100% width for pull classes\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    .box-shadow(none);\n  }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  .border-top-radius(@navbar-border-radius);\n  .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n  .navbar-vertical-align(@input-height-base);\n\n  &.btn-sm {\n    .navbar-vertical-align(@input-height-small);\n  }\n  &.btn-xs {\n    .navbar-vertical-align(22);\n  }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n  .navbar-vertical-align(@line-height-computed);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin-left: @navbar-padding-horizontal;\n    margin-right: @navbar-padding-horizontal;\n  }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-left  { .pull-left(); }\n  .navbar-right {\n    .pull-right();\n    margin-right: -@navbar-padding-horizontal;\n\n    ~ .navbar-right {\n      margin-right: 0;\n    }\n  }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  background-color: @navbar-default-bg;\n  border-color: @navbar-default-border;\n\n  .navbar-brand {\n    color: @navbar-default-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-brand-hover-color;\n      background-color: @navbar-default-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-default-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-default-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-hover-color;\n        background-color: @navbar-default-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-active-color;\n        background-color: @navbar-default-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n        background-color: @navbar-default-link-disabled-bg;\n      }\n    }\n  }\n\n  .navbar-toggle {\n    border-color: @navbar-default-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-default-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-default-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: @navbar-default-border;\n  }\n\n  // Dropdown menu items\n  .navbar-nav {\n    // Remove background color from open dropdown\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-default-link-active-bg;\n        color: @navbar-default-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display when collapsed\n      .open .dropdown-menu {\n        > li > a {\n          color: @navbar-default-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-hover-color;\n            background-color: @navbar-default-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-active-color;\n            background-color: @navbar-default-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-disabled-color;\n            background-color: @navbar-default-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n\n  // Links in navbars\n  //\n  // Add a class to ensure links outside the navbar nav are colored correctly.\n\n  .navbar-link {\n    color: @navbar-default-link-color;\n    &:hover {\n      color: @navbar-default-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-default-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n      }\n    }\n  }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n  background-color: @navbar-inverse-bg;\n  border-color: @navbar-inverse-border;\n\n  .navbar-brand {\n    color: @navbar-inverse-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-brand-hover-color;\n      background-color: @navbar-inverse-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-inverse-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-inverse-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-hover-color;\n        background-color: @navbar-inverse-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-active-color;\n        background-color: @navbar-inverse-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n        background-color: @navbar-inverse-link-disabled-bg;\n      }\n    }\n  }\n\n  // Darken the responsive nav toggle\n  .navbar-toggle {\n    border-color: @navbar-inverse-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-inverse-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-inverse-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: darken(@navbar-inverse-bg, 7%);\n  }\n\n  // Dropdowns\n  .navbar-nav {\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-inverse-link-active-bg;\n        color: @navbar-inverse-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display\n      .open .dropdown-menu {\n        > .dropdown-header {\n          border-color: @navbar-inverse-border;\n        }\n        .divider {\n          background-color: @navbar-inverse-border;\n        }\n        > li > a {\n          color: @navbar-inverse-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-hover-color;\n            background-color: @navbar-inverse-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-active-color;\n            background-color: @navbar-inverse-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-disabled-color;\n            background-color: @navbar-inverse-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n  .navbar-link {\n    color: @navbar-inverse-link-color;\n    &:hover {\n      color: @navbar-inverse-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-inverse-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n      }\n    }\n  }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n  margin-top: ((@navbar-height - @element-height) / 2);\n  margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n  .clearfix();\n}\n.center-block {\n  .center-block();\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n  display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n  position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n  padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n  margin-bottom: @line-height-computed;\n  list-style: none;\n  background-color: @breadcrumb-bg;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline-block;\n\n    + li:before {\n      content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n      padding: 0 5px;\n      color: @breadcrumb-color;\n    }\n  }\n\n  > .active {\n    color: @breadcrumb-active-color;\n  }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline; // Remove list-style and block-level defaults\n    > a,\n    > span {\n      position: relative;\n      float: left; // Collapse white-space\n      padding: @padding-base-vertical @padding-base-horizontal;\n      line-height: @line-height-base;\n      text-decoration: none;\n      color: @pagination-color;\n      background-color: @pagination-bg;\n      border: 1px solid @pagination-border;\n      margin-left: -1px;\n    }\n    &:first-child {\n      > a,\n      > span {\n        margin-left: 0;\n        .border-left-radius(@border-radius-base);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius-base);\n      }\n    }\n  }\n\n  > li > a,\n  > li > span {\n    &:hover,\n    &:focus {\n      z-index: 3;\n      color: @pagination-hover-color;\n      background-color: @pagination-hover-bg;\n      border-color: @pagination-hover-border;\n    }\n  }\n\n  > .active > a,\n  > .active > span {\n    &,\n    &:hover,\n    &:focus {\n      z-index: 2;\n      color: @pagination-active-color;\n      background-color: @pagination-active-bg;\n      border-color: @pagination-active-border;\n      cursor: default;\n    }\n  }\n\n  > .disabled {\n    > span,\n    > span:hover,\n    > span:focus,\n    > a,\n    > a:hover,\n    > a:focus {\n      color: @pagination-disabled-color;\n      background-color: @pagination-disabled-bg;\n      border-color: @pagination-disabled-border;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n  .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n  .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  > li {\n    > a,\n    > span {\n      padding: @padding-vertical @padding-horizontal;\n      font-size: @font-size;\n      line-height: @line-height;\n    }\n    &:first-child {\n      > a,\n      > span {\n        .border-left-radius(@border-radius);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius);\n      }\n    }\n  }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  list-style: none;\n  text-align: center;\n  &:extend(.clearfix all);\n  li {\n    display: inline;\n    > a,\n    > span {\n      display: inline-block;\n      padding: 5px 14px;\n      background-color: @pager-bg;\n      border: 1px solid @pager-border;\n      border-radius: @pager-border-radius;\n    }\n\n    > a:hover,\n    > a:focus {\n      text-decoration: none;\n      background-color: @pager-hover-bg;\n    }\n  }\n\n  .next {\n    > a,\n    > span {\n      float: right;\n    }\n  }\n\n  .previous {\n    > a,\n    > span {\n      float: left;\n    }\n  }\n\n  .disabled {\n    > a,\n    > a:hover,\n    > a:focus,\n    > span {\n      color: @pager-disabled-color;\n      background-color: @pager-bg;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: @label-color;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n\n  // Add hover effects, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @label-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Empty labels collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for labels in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n  .label-variant(@label-default-bg);\n}\n\n.label-primary {\n  .label-variant(@label-primary-bg);\n}\n\n.label-success {\n  .label-variant(@label-success-bg);\n}\n\n.label-info {\n  .label-variant(@label-info-bg);\n}\n\n.label-warning {\n  .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n  .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n  background-color: @color;\n\n  &[href] {\n    &:hover,\n    &:focus {\n      background-color: darken(@color, 10%);\n    }\n  }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: @font-size-small;\n  font-weight: @badge-font-weight;\n  color: @badge-color;\n  line-height: @badge-line-height;\n  vertical-align: middle;\n  white-space: nowrap;\n  text-align: center;\n  background-color: @badge-bg;\n  border-radius: @badge-border-radius;\n\n  // Empty badges collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for badges in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n\n  .btn-xs &,\n  .btn-group-xs > .btn & {\n    top: 0;\n    padding: 1px 5px;\n  }\n\n  // Hover state, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @badge-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Account for badges in navs\n  .list-group-item.active > &,\n  .nav-pills > .active > a > & {\n    color: @badge-active-color;\n    background-color: @badge-active-bg;\n  }\n\n  .list-group-item > & {\n    float: right;\n  }\n\n  .list-group-item > & + & {\n    margin-right: 5px;\n  }\n\n  .nav-pills > li > a > & {\n    margin-left: 3px;\n  }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n  padding-top:    @jumbotron-padding;\n  padding-bottom: @jumbotron-padding;\n  margin-bottom: @jumbotron-padding;\n  color: @jumbotron-color;\n  background-color: @jumbotron-bg;\n\n  h1,\n  .h1 {\n    color: @jumbotron-heading-color;\n  }\n\n  p {\n    margin-bottom: (@jumbotron-padding / 2);\n    font-size: @jumbotron-font-size;\n    font-weight: 200;\n  }\n\n  > hr {\n    border-top-color: darken(@jumbotron-bg, 10%);\n  }\n\n  .container &,\n  .container-fluid & {\n    border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n  }\n\n  .container {\n    max-width: 100%;\n  }\n\n  @media screen and (min-width: @screen-sm-min) {\n    padding-top:    (@jumbotron-padding * 1.6);\n    padding-bottom: (@jumbotron-padding * 1.6);\n\n    .container &,\n    .container-fluid & {\n      padding-left:  (@jumbotron-padding * 2);\n      padding-right: (@jumbotron-padding * 2);\n    }\n\n    h1,\n    .h1 {\n      font-size: @jumbotron-heading-font-size;\n    }\n  }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n  display: block;\n  padding: @thumbnail-padding;\n  margin-bottom: @line-height-computed;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(border .2s ease-in-out);\n\n  > img,\n  a > img {\n    &:extend(.img-responsive);\n    margin-left: auto;\n    margin-right: auto;\n  }\n\n  // Add a hover state for linked versions only\n  a&:hover,\n  a&:focus,\n  a&.active {\n    border-color: @link-color;\n  }\n\n  // Image captions\n  .caption {\n    padding: @thumbnail-caption-padding;\n    color: @thumbnail-caption-color;\n  }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n  padding: @alert-padding;\n  margin-bottom: @line-height-computed;\n  border: 1px solid transparent;\n  border-radius: @alert-border-radius;\n\n  // Headings for larger alerts\n  h4 {\n    margin-top: 0;\n    // Specified for the h4 to prevent conflicts of changing @headings-color\n    color: inherit;\n  }\n\n  // Provide class for links that match alerts\n  .alert-link {\n    font-weight: @alert-link-font-weight;\n  }\n\n  // Improve alignment and spacing of inner content\n  > p,\n  > ul {\n    margin-bottom: 0;\n  }\n\n  > p + p {\n    margin-top: 5px;\n  }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n  padding-right: (@alert-padding + 20);\n\n  // Adjust close link position\n  .close {\n    position: relative;\n    top: -2px;\n    right: -21px;\n    color: inherit;\n  }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n  .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n  .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n  .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n  .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n  background-color: @background;\n  border-color: @border;\n  color: @text-color;\n\n  hr {\n    border-top-color: darken(@border, 5%);\n  }\n  .alert-link {\n    color: darken(@text-color, 10%);\n  }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n  overflow: hidden;\n  height: @line-height-computed;\n  margin-bottom: @line-height-computed;\n  background-color: @progress-bg;\n  border-radius: @progress-border-radius;\n  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: @font-size-small;\n  line-height: @line-height-computed;\n  color: @progress-bar-color;\n  text-align: center;\n  background-color: @progress-bar-bg;\n  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n  .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  #gradient > .striped();\n  background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n  .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n  .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n  .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n  .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n  .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n    background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Opera 12\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n  background-color: @color;\n\n  // Deprecated parent class requirement as of v3.2.0\n  .progress-striped & {\n    #gradient > .striped();\n  }\n}\n",".media {\n  // Proper spacing between instances of .media\n  margin-top: 15px;\n\n  &:first-child {\n    margin-top: 0;\n  }\n}\n\n.media,\n.media-body {\n  zoom: 1;\n  overflow: hidden;\n}\n\n.media-body {\n  width: 10000px;\n}\n\n.media-object {\n  display: block;\n\n  // Fix collapse in webkit from max-width: 100% and display: table-cell.\n  &.img-thumbnail {\n    max-width: none;\n  }\n}\n\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n\n.media-middle {\n  vertical-align: middle;\n}\n\n.media-bottom {\n  vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on <ul>, <ol>, or <div>.\n\n.list-group {\n  // No need to set list-style: none; since .list-group-item is block level\n  margin-bottom: 20px;\n  padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  // Place the border on the list items and negative margin up for better styling\n  margin-bottom: -1px;\n  background-color: @list-group-bg;\n  border: 1px solid @list-group-border;\n\n  // Round the first and last items\n  &:first-child {\n    .border-top-radius(@list-group-border-radius);\n  }\n  &:last-child {\n    margin-bottom: 0;\n    .border-bottom-radius(@list-group-border-radius);\n  }\n}\n\n\n// Interactive list items\n//\n// Use anchor or button elements instead of `li`s or `div`s to create interactive items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item,\nbutton.list-group-item {\n  color: @list-group-link-color;\n\n  .list-group-item-heading {\n    color: @list-group-link-heading-color;\n  }\n\n  // Hover state\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @list-group-link-hover-color;\n    background-color: @list-group-hover-bg;\n  }\n}\n\nbutton.list-group-item {\n  width: 100%;\n  text-align: left;\n}\n\n.list-group-item {\n  // Disabled state\n  &.disabled,\n  &.disabled:hover,\n  &.disabled:focus {\n    background-color: @list-group-disabled-bg;\n    color: @list-group-disabled-color;\n    cursor: @cursor-disabled;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-disabled-text-color;\n    }\n  }\n\n  // Active class on item itself, not parent\n  &.active,\n  &.active:hover,\n  &.active:focus {\n    z-index: 2; // Place active items above their siblings for proper border styling\n    color: @list-group-active-color;\n    background-color: @list-group-active-bg;\n    border-color: @list-group-active-border;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading,\n    .list-group-item-heading > small,\n    .list-group-item-heading > .small {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-active-text-color;\n    }\n  }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n","// List Groups\n\n.list-group-item-variant(@state; @background; @color) {\n  .list-group-item-@{state} {\n    color: @color;\n    background-color: @background;\n\n    a&,\n    button& {\n      color: @color;\n\n      .list-group-item-heading {\n        color: inherit;\n      }\n\n      &:hover,\n      &:focus {\n        color: @color;\n        background-color: darken(@background, 5%);\n      }\n      &.active,\n      &.active:hover,\n      &.active:focus {\n        color: #fff;\n        background-color: @color;\n        border-color: @color;\n      }\n    }\n  }\n}\n","//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n  margin-bottom: @line-height-computed;\n  background-color: @panel-bg;\n  border: 1px solid transparent;\n  border-radius: @panel-border-radius;\n  .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n  padding: @panel-body-padding;\n  &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n  padding: @panel-heading-padding;\n  border-bottom: 1px solid transparent;\n  .border-top-radius((@panel-border-radius - 1));\n\n  > .dropdown .dropdown-toggle {\n    color: inherit;\n  }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: ceil((@font-size-base * 1.125));\n  color: inherit;\n\n  > a,\n  > small,\n  > .small,\n  > small > a,\n  > .small > a {\n    color: inherit;\n  }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n  padding: @panel-footer-padding;\n  background-color: @panel-footer-bg;\n  border-top: 1px solid @panel-inner-border;\n  .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n  > .list-group,\n  > .panel-collapse > .list-group {\n    margin-bottom: 0;\n\n    .list-group-item {\n      border-width: 1px 0;\n      border-radius: 0;\n    }\n\n    // Add border top radius for first one\n    &:first-child {\n      .list-group-item:first-child {\n        border-top: 0;\n        .border-top-radius((@panel-border-radius - 1));\n      }\n    }\n\n    // Add border bottom radius for last one\n    &:last-child {\n      .list-group-item:last-child {\n        border-bottom: 0;\n        .border-bottom-radius((@panel-border-radius - 1));\n      }\n    }\n  }\n  > .panel-heading + .panel-collapse > .list-group {\n    .list-group-item:first-child {\n      .border-top-radius(0);\n    }\n  }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n  .list-group-item:first-child {\n    border-top-width: 0;\n  }\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n  > .table,\n  > .table-responsive > .table,\n  > .panel-collapse > .table {\n    margin-bottom: 0;\n\n    caption {\n      padding-left: @panel-body-padding;\n      padding-right: @panel-body-padding;\n    }\n  }\n  // Add border top radius for first one\n  > .table:first-child,\n  > .table-responsive:first-child > .table:first-child {\n    .border-top-radius((@panel-border-radius - 1));\n\n    > thead:first-child,\n    > tbody:first-child {\n      > tr:first-child {\n        border-top-left-radius: (@panel-border-radius - 1);\n        border-top-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-top-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-top-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  // Add border bottom radius for last one\n  > .table:last-child,\n  > .table-responsive:last-child > .table:last-child {\n    .border-bottom-radius((@panel-border-radius - 1));\n\n    > tbody:last-child,\n    > tfoot:last-child {\n      > tr:last-child {\n        border-bottom-left-radius: (@panel-border-radius - 1);\n        border-bottom-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-bottom-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-bottom-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  > .panel-body + .table,\n  > .panel-body + .table-responsive,\n  > .table + .panel-body,\n  > .table-responsive + .panel-body {\n    border-top: 1px solid @table-border-color;\n  }\n  > .table > tbody:first-child > tr:first-child th,\n  > .table > tbody:first-child > tr:first-child td {\n    border-top: 0;\n  }\n  > .table-bordered,\n  > .table-responsive > .table-bordered {\n    border: 0;\n    > thead,\n    > tbody,\n    > tfoot {\n      > tr {\n        > th:first-child,\n        > td:first-child {\n          border-left: 0;\n        }\n        > th:last-child,\n        > td:last-child {\n          border-right: 0;\n        }\n      }\n    }\n    > thead,\n    > tbody {\n      > tr:first-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n    > tbody,\n    > tfoot {\n      > tr:last-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n  }\n  > .table-responsive {\n    border: 0;\n    margin-bottom: 0;\n  }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n  margin-bottom: @line-height-computed;\n\n  // Tighten up margin so it's only between panels\n  .panel {\n    margin-bottom: 0;\n    border-radius: @panel-border-radius;\n\n    + .panel {\n      margin-top: 5px;\n    }\n  }\n\n  .panel-heading {\n    border-bottom: 0;\n\n    + .panel-collapse > .panel-body,\n    + .panel-collapse > .list-group {\n      border-top: 1px solid @panel-inner-border;\n    }\n  }\n\n  .panel-footer {\n    border-top: 0;\n    + .panel-collapse .panel-body {\n      border-bottom: 1px solid @panel-inner-border;\n    }\n  }\n}\n\n\n// Contextual variations\n.panel-default {\n  .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n  .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n  .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n  .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n  .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n  .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n","// Panels\n\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n  border-color: @border;\n\n  & > .panel-heading {\n    color: @heading-text-color;\n    background-color: @heading-bg-color;\n    border-color: @heading-border;\n\n    + .panel-collapse > .panel-body {\n      border-top-color: @border;\n    }\n    .badge {\n      color: @heading-bg-color;\n      background-color: @heading-text-color;\n    }\n  }\n  & > .panel-footer {\n    + .panel-collapse > .panel-body {\n      border-bottom-color: @border;\n    }\n  }\n}\n","// Embeds responsive\n//\n// Credit: Nicolas Gallagher and SUIT CSS.\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n\n  .embed-responsive-item,\n  iframe,\n  embed,\n  object,\n  video {\n    position: absolute;\n    top: 0;\n    left: 0;\n    bottom: 0;\n    height: 100%;\n    width: 100%;\n    border: 0;\n  }\n}\n\n// Modifier class for 16:9 aspect ratio\n.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n\n// Modifier class for 4:3 aspect ratio\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n","//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: @well-bg;\n  border: 1px solid @well-border;\n  border-radius: @border-radius-base;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n  blockquote {\n    border-color: #ddd;\n    border-color: rgba(0,0,0,.15);\n  }\n}\n\n// Sizes\n.well-lg {\n  padding: 24px;\n  border-radius: @border-radius-large;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: @border-radius-small;\n}\n","//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n  float: right;\n  font-size: (@font-size-base * 1.5);\n  font-weight: @close-font-weight;\n  line-height: 1;\n  color: @close-color;\n  text-shadow: @close-text-shadow;\n  .opacity(.2);\n\n  &:hover,\n  &:focus {\n    color: @close-color;\n    text-decoration: none;\n    cursor: pointer;\n    .opacity(.5);\n  }\n\n  // Additional properties for button version\n  // iOS requires the button element instead of an anchor tag.\n  // If you want the anchor version, it requires `href=\"#\"`.\n  // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n  button& {\n    padding: 0;\n    cursor: pointer;\n    background: transparent;\n    border: 0;\n    -webkit-appearance: none;\n  }\n}\n","//\n// Modals\n// --------------------------------------------------\n\n// .modal-open      - body class for killing the scroll\n// .modal           - container to scroll within\n// .modal-dialog    - positioning shell for the actual modal\n// .modal-content   - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n  overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal;\n  -webkit-overflow-scrolling: touch;\n\n  // Prevent Chrome on Windows from adding a focus outline. For details, see\n  // https://github.com/twbs/bootstrap/pull/10951.\n  outline: 0;\n\n  // When fading in the modal, animate it to slide down\n  &.fade .modal-dialog {\n    .translate(0, -25%);\n    .transition-transform(~\"0.3s ease-out\");\n  }\n  &.in .modal-dialog { .translate(0, 0) }\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n  position: relative;\n  background-color: @modal-content-bg;\n  border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n  border: 1px solid @modal-content-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 3px 9px rgba(0,0,0,.5));\n  background-clip: padding-box;\n  // Remove focus outline from opened modal\n  outline: 0;\n}\n\n// Modal background\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal-background;\n  background-color: @modal-backdrop-bg;\n  // Fade for backdrop\n  &.fade { .opacity(0); }\n  &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n  padding: @modal-title-padding;\n  border-bottom: 1px solid @modal-header-border-color;\n  min-height: (@modal-title-padding + @modal-title-line-height);\n}\n// Close icon\n.modal-header .close {\n  margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n  margin: 0;\n  line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n  position: relative;\n  padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n  padding: @modal-inner-padding;\n  text-align: right; // right align buttons\n  border-top: 1px solid @modal-footer-border-color;\n  &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n  // Properly space out buttons\n  .btn + .btn {\n    margin-left: 5px;\n    margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n  }\n  // but override that for button groups\n  .btn-group .btn + .btn {\n    margin-left: -1px;\n  }\n  // and override it for block buttons as well\n  .btn-block + .btn-block {\n    margin-left: 0;\n  }\n}\n\n// Measure scrollbar width for padding body during modal show/hide\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n  // Automatically set modal's width for larger viewports\n  .modal-dialog {\n    width: @modal-md;\n    margin: 30px auto;\n  }\n  .modal-content {\n    .box-shadow(0 5px 15px rgba(0,0,0,.5));\n  }\n\n  // Modal sizes\n  .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n  .modal-lg { width: @modal-lg; }\n}\n","//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n  position: absolute;\n  z-index: @zindex-tooltip;\n  display: block;\n  // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.\n  // So reset our font and text properties to avoid inheriting weird values.\n  .reset-text();\n  font-size: @font-size-small;\n\n  .opacity(0);\n\n  &.in     { .opacity(@tooltip-opacity); }\n  &.top    { margin-top:  -3px; padding: @tooltip-arrow-width 0; }\n  &.right  { margin-left:  3px; padding: 0 @tooltip-arrow-width; }\n  &.bottom { margin-top:   3px; padding: @tooltip-arrow-width 0; }\n  &.left   { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n  max-width: @tooltip-max-width;\n  padding: 3px 8px;\n  color: @tooltip-color;\n  text-align: center;\n  background-color: @tooltip-bg;\n  border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1\n.tooltip {\n  &.top .tooltip-arrow {\n    bottom: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-left .tooltip-arrow {\n    bottom: 0;\n    right: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-right .tooltip-arrow {\n    bottom: 0;\n    left: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.right .tooltip-arrow {\n    top: 50%;\n    left: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-right-color: @tooltip-arrow-color;\n  }\n  &.left .tooltip-arrow {\n    top: 50%;\n    right: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-left-color: @tooltip-arrow-color;\n  }\n  &.bottom .tooltip-arrow {\n    top: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-left .tooltip-arrow {\n    top: 0;\n    right: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-right .tooltip-arrow {\n    top: 0;\n    left: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n}\n",".reset-text() {\n  font-family: @font-family-base;\n  // We deliberately do NOT reset font-size.\n  font-style: normal;\n  font-weight: normal;\n  letter-spacing: normal;\n  line-break: auto;\n  line-height: @line-height-base;\n  text-align: left; // Fallback for where `start` is not supported\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  white-space: normal;\n  word-break: normal;\n  word-spacing: normal;\n  word-wrap: normal;\n}\n","//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: @zindex-popover;\n  display: none;\n  max-width: @popover-max-width;\n  padding: 1px;\n  // Our parent element can be arbitrary since popovers are by default inserted as a sibling of their target element.\n  // So reset our font and text properties to avoid inheriting weird values.\n  .reset-text();\n  font-size: @font-size-base;\n\n  background-color: @popover-bg;\n  background-clip: padding-box;\n  border: 1px solid @popover-fallback-border-color;\n  border: 1px solid @popover-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n  // Offset the popover to account for the popover arrow\n  &.top     { margin-top: -@popover-arrow-width; }\n  &.right   { margin-left: @popover-arrow-width; }\n  &.bottom  { margin-top: @popover-arrow-width; }\n  &.left    { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n  margin: 0; // reset heading margin\n  padding: 8px 14px;\n  font-size: @font-size-base;\n  background-color: @popover-title-bg;\n  border-bottom: 1px solid darken(@popover-title-bg, 5%);\n  border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n  &,\n  &:after {\n    position: absolute;\n    display: block;\n    width: 0;\n    height: 0;\n    border-color: transparent;\n    border-style: solid;\n  }\n}\n.popover > .arrow {\n  border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n  border-width: @popover-arrow-width;\n  content: \"\";\n}\n\n.popover {\n  &.top > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-bottom-width: 0;\n    border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-top-color: @popover-arrow-outer-color;\n    bottom: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      bottom: 1px;\n      margin-left: -@popover-arrow-width;\n      border-bottom-width: 0;\n      border-top-color: @popover-arrow-color;\n    }\n  }\n  &.right > .arrow {\n    top: 50%;\n    left: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-left-width: 0;\n    border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-right-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      left: 1px;\n      bottom: -@popover-arrow-width;\n      border-left-width: 0;\n      border-right-color: @popover-arrow-color;\n    }\n  }\n  &.bottom > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-top-width: 0;\n    border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-bottom-color: @popover-arrow-outer-color;\n    top: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      top: 1px;\n      margin-left: -@popover-arrow-width;\n      border-top-width: 0;\n      border-bottom-color: @popover-arrow-color;\n    }\n  }\n\n  &.left > .arrow {\n    top: 50%;\n    right: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-right-width: 0;\n    border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-left-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      right: 1px;\n      border-right-width: 0;\n      border-left-color: @popover-arrow-color;\n      bottom: -@popover-arrow-width;\n    }\n  }\n}\n","//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n\n  > .item {\n    display: none;\n    position: relative;\n    .transition(.6s ease-in-out left);\n\n    // Account for jankitude on images\n    > img,\n    > a > img {\n      &:extend(.img-responsive);\n      line-height: 1;\n    }\n\n    // WebKit CSS3 transforms for supported devices\n    @media all and (transform-3d), (-webkit-transform-3d) {\n      .transition-transform(~'0.6s ease-in-out');\n      .backface-visibility(~'hidden');\n      .perspective(1000px);\n\n      &.next,\n      &.active.right {\n        .translate3d(100%, 0, 0);\n        left: 0;\n      }\n      &.prev,\n      &.active.left {\n        .translate3d(-100%, 0, 0);\n        left: 0;\n      }\n      &.next.left,\n      &.prev.right,\n      &.active {\n        .translate3d(0, 0, 0);\n        left: 0;\n      }\n    }\n  }\n\n  > .active,\n  > .next,\n  > .prev {\n    display: block;\n  }\n\n  > .active {\n    left: 0;\n  }\n\n  > .next,\n  > .prev {\n    position: absolute;\n    top: 0;\n    width: 100%;\n  }\n\n  > .next {\n    left: 100%;\n  }\n  > .prev {\n    left: -100%;\n  }\n  > .next.left,\n  > .prev.right {\n    left: 0;\n  }\n\n  > .active.left {\n    left: -100%;\n  }\n  > .active.right {\n    left: 100%;\n  }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: @carousel-control-width;\n  .opacity(@carousel-control-opacity);\n  font-size: @carousel-control-font-size;\n  color: @carousel-control-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  // We can't have this transition here because WebKit cancels the carousel\n  // animation if you trip this while in the middle of another animation.\n\n  // Set gradients for backgrounds\n  &.left {\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n  }\n  &.right {\n    left: auto;\n    right: 0;\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n  }\n\n  // Hover/focus state\n  &:hover,\n  &:focus {\n    outline: 0;\n    color: @carousel-control-color;\n    text-decoration: none;\n    .opacity(.9);\n  }\n\n  // Toggles\n  .icon-prev,\n  .icon-next,\n  .glyphicon-chevron-left,\n  .glyphicon-chevron-right {\n    position: absolute;\n    top: 50%;\n    margin-top: -10px;\n    z-index: 5;\n    display: inline-block;\n  }\n  .icon-prev,\n  .glyphicon-chevron-left {\n    left: 50%;\n    margin-left: -10px;\n  }\n  .icon-next,\n  .glyphicon-chevron-right {\n    right: 50%;\n    margin-right: -10px;\n  }\n  .icon-prev,\n  .icon-next {\n    width:  20px;\n    height: 20px;\n    line-height: 1;\n    font-family: serif;\n  }\n\n\n  .icon-prev {\n    &:before {\n      content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n    }\n  }\n  .icon-next {\n    &:before {\n      content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n    }\n  }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n\n  li {\n    display: inline-block;\n    width:  10px;\n    height: 10px;\n    margin: 1px;\n    text-indent: -999px;\n    border: 1px solid @carousel-indicator-border-color;\n    border-radius: 10px;\n    cursor: pointer;\n\n    // IE8-9 hack for event handling\n    //\n    // Internet Explorer 8-9 does not support clicks on elements without a set\n    // `background-color`. We cannot use `filter` since that's not viewed as a\n    // background color by the browser. Thus, a hack is needed.\n    // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer\n    //\n    // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n    // set alpha transparency for the best results possible.\n    background-color: #000 \\9; // IE8\n    background-color: rgba(0,0,0,0); // IE9\n  }\n  .active {\n    margin: 0;\n    width:  12px;\n    height: 12px;\n    background-color: @carousel-indicator-active-bg;\n  }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: @carousel-caption-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  & .btn {\n    text-shadow: none; // No shadow for button elements in carousel-caption\n  }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n  // Scale up the controls a smidge\n  .carousel-control {\n    .glyphicon-chevron-left,\n    .glyphicon-chevron-right,\n    .icon-prev,\n    .icon-next {\n      width: 30px;\n      height: 30px;\n      margin-top: -15px;\n      font-size: 30px;\n    }\n    .glyphicon-chevron-left,\n    .icon-prev {\n      margin-left: -15px;\n    }\n    .glyphicon-chevron-right,\n    .icon-next {\n      margin-right: -15px;\n    }\n  }\n\n  // Show and left align the captions\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n\n  // Move up the indicators\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n","// Clearfix\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n//    contenteditable attribute is included anywhere else in the document.\n//    Otherwise it causes space to appear at the top and bottom of elements\n//    that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n//    `:before` to contain the top-margins of child elements.\n//\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n\n.clearfix() {\n  &:before,\n  &:after {\n    content: \" \"; // 1\n    display: table; // 2\n  }\n  &:after {\n    clear: both;\n  }\n}\n","// Center-align a block level element\n\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n","// CSS image replacement\n//\n// Heads up! v3 launched with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n  font: ~\"0/0\" a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n// New mixin to use as of v3.0.1\n.text-hide() {\n  .hide-text();\n}\n","//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#support-ie10-width\n// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n  width: device-width;\n}\n\n\n// Visibility utilities\n// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  .responsive-invisibility();\n}\n\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n\n.visible-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-visibility();\n  }\n}\n.visible-xs-block {\n  @media (max-width: @screen-xs-max) {\n    display: block !important;\n  }\n}\n.visible-xs-inline {\n  @media (max-width: @screen-xs-max) {\n    display: inline !important;\n  }\n}\n.visible-xs-inline-block {\n  @media (max-width: @screen-xs-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-visibility();\n  }\n}\n.visible-sm-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: block !important;\n  }\n}\n.visible-sm-inline {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline !important;\n  }\n}\n.visible-sm-inline-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-visibility();\n  }\n}\n.visible-md-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: block !important;\n  }\n}\n.visible-md-inline {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline !important;\n  }\n}\n.visible-md-inline-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-visibility();\n  }\n}\n.visible-lg-block {\n  @media (min-width: @screen-lg-min) {\n    display: block !important;\n  }\n}\n.visible-lg-inline {\n  @media (min-width: @screen-lg-min) {\n    display: inline !important;\n  }\n}\n.visible-lg-inline-block {\n  @media (min-width: @screen-lg-min) {\n    display: inline-block !important;\n  }\n}\n\n.hidden-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-invisibility();\n  }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n// Note: Deprecated .visible-print as of v3.2.0\n.visible-print {\n  .responsive-invisibility();\n\n  @media print {\n    .responsive-visibility();\n  }\n}\n.visible-print-block {\n  display: none !important;\n\n  @media print {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n\n  @media print {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n\n  @media print {\n    display: inline-block !important;\n  }\n}\n\n.hidden-print {\n  @media print {\n    .responsive-invisibility();\n  }\n}\n","// Responsive utilities\n\n//\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n  display: block !important;\n  table&  { display: table !important; }\n  tr&     { display: table-row !important; }\n  th&,\n  td&     { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n  display: none !important;\n}\n"]}
\ No newline at end of file
diff --git a/web/root/bootstrap/css/bootstrap.min.css b/web/root/bootstrap/css/bootstrap.min.css
new file mode 100644
index 0000000000..d65c66b1ba
--- /dev/null
+++ b/web/root/bootstrap/css/bootstrap.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.eot b/web/root/bootstrap/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000000000000000000000000000000000000..b93a4953fff68df523aa7656497ee339d6026d64
GIT binary patch
literal 20127
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

literal 0
HcmV?d00001

diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.svg b/web/root/bootstrap/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000000..94fb5490a2
--- /dev/null
+++ b/web/root/bootstrap/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,288 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#xa5;" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode="&#x2000;" horiz-adv-x="650" />
+<glyph unicode="&#x2001;" horiz-adv-x="1300" />
+<glyph unicode="&#x2002;" horiz-adv-x="650" />
+<glyph unicode="&#x2003;" horiz-adv-x="1300" />
+<glyph unicode="&#x2004;" horiz-adv-x="433" />
+<glyph unicode="&#x2005;" horiz-adv-x="325" />
+<glyph unicode="&#x2006;" horiz-adv-x="216" />
+<glyph unicode="&#x2007;" horiz-adv-x="216" />
+<glyph unicode="&#x2008;" horiz-adv-x="162" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="325" />
+<glyph unicode="&#x20ac;" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="&#x20bd;" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="&#x2212;" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#x231b;" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="&#x26fa;" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="&#x2709;" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="&#x270f;" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="&#xe001;" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="&#xe002;" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="&#xe003;" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="&#xe005;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="&#xe006;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="&#xe007;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="&#xe008;" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="&#xe009;" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="&#xe010;" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe011;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="&#xe012;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe013;" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="&#xe014;" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="&#xe015;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe016;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe017;" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="&#xe018;" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe019;" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="&#xe020;" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="&#xe021;" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe022;" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="&#xe023;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe024;" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe026;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="&#xe027;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="&#xe028;" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="&#xe029;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe030;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="&#xe031;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="&#xe032;" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe033;" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="&#xe034;" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="&#xe035;" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="&#xe036;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="&#xe037;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="&#xe038;" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="&#xe039;" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="&#xe040;" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="&#xe041;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe042;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe043;" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="&#xe044;" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe045;" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="&#xe046;" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="&#xe047;" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="&#xe048;" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="&#xe049;" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="&#xe050;" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="&#xe051;" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="&#xe052;" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe053;" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe054;" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe055;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe056;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe057;" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe058;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe059;" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="&#xe062;" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="&#xe063;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="&#xe064;" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="&#xe065;" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="&#xe066;" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="&#xe067;" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="&#xe068;" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe069;" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe070;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe071;" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="&#xe072;" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="&#xe073;" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe074;" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe075;" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="&#xe076;" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe077;" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe078;" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe079;" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="&#xe080;" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="&#xe081;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe082;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe083;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="&#xe084;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="&#xe085;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe086;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe087;" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="&#xe088;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe089;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe090;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="&#xe091;" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="&#xe092;" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe093;" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="&#xe094;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe095;" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe096;" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="&#xe097;" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="&#xe101;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe102;" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="&#xe103;" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="&#xe104;" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="&#xe105;" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe106;" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe107;" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="&#xe108;" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="&#xe109;" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="&#xe110;" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="&#xe111;" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="&#xe112;" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="&#xe113;" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="&#xe114;" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="&#xe115;" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe116;" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="&#xe117;" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="&#xe118;" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="&#xe119;" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe120;" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="&#xe121;" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="&#xe122;" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="&#xe123;" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="&#xe124;" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="&#xe125;" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe126;" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="&#xe127;" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe128;" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe129;" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe130;" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="&#xe131;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="&#xe132;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="&#xe133;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="&#xe134;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe135;" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="&#xe136;" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="&#xe138;" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="&#xe139;" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="&#xe140;" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="&#xe141;" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="&#xe142;" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="&#xe143;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="&#xe144;" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="&#xe145;" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="&#xe146;" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="&#xe148;" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="&#xe149;" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="&#xe150;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe151;" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="&#xe152;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="&#xe153;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="&#xe154;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="&#xe155;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="&#xe158;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe159;" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="&#xe160;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="&#xe161;" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe162;" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="&#xe163;" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe164;" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="&#xe165;" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="&#xe166;" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe167;" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe168;" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="&#xe169;" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe170;" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe171;" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="&#xe172;" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="&#xe173;" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe174;" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="&#xe175;" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe176;" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe177;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="&#xe178;" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="&#xe179;" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="&#xe180;" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="&#xe181;" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="&#xe182;" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="&#xe183;" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="&#xe184;" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe185;" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="&#xe186;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe187;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe188;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="&#xe189;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="&#xe190;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="&#xe191;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe192;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe193;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="&#xe194;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="&#xe195;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="&#xe197;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe198;" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="&#xe199;" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="&#xe200;" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="&#xe201;" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="&#xe202;" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="&#xe203;" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="&#xe204;" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="&#xe205;" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe206;" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe209;" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="&#xe210;" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe211;" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe212;" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe213;" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe214;" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe215;" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe216;" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="&#xe218;" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="&#xe219;" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="&#xe221;" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe223;" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="&#xe224;" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="&#xe225;" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe226;" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="&#xe227;" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="&#xe230;" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="&#xe231;" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe232;" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe233;" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="&#xe234;" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe235;" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe236;" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe237;" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="&#xe238;" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe239;" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="&#xe240;" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="&#xe241;" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="&#xe242;" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe243;" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="&#xe244;" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="&#xe245;" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="&#xe246;" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="&#xe247;" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe248;" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="&#xe249;" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe250;" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="&#xe251;" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="&#xe252;" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="&#xe253;" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="&#xe254;" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="&#xe255;" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="&#xe256;" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="&#xe257;" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="&#xe258;" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="&#xe259;" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="&#xe260;" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="&#xf8ff;" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg> 
\ No newline at end of file
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.ttf b/web/root/bootstrap/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..1413fc609ab6f21774de0cb7e01360095584f65b
GIT binary patch
literal 45404
zcmZQzWME+6XJ}wxW+-rT3-Qg>ntq3Yf#DAW1A~mai>n)>3j+%S1ET~31A~Hpu)b0H
z&g@PG21W}828M*>+{A)gYacf<Ffh(wU|=gsE-O)B&|vz(z`()7z`($go>*M)|33pW
z0|SQu0|Ns`dQN3pZS0|Z1_llZ28Jn1GEx&$Sbg<xFfh#7!@$5`mXVQ~$mPN!$iOh?
z3j+g#N=9x;#iip42@DK#B^Vf(OLFp)6Z3yhJ;%VXpof8h$sjkeqJS}r;RXZ4B9MNC
zyu{qp%{mKaGcYW=!@$72s35<%WY^-SrVI?r8W<S3E|#Yzr5%<C31DE@7{b87z{bD;
z_C3QH+ePOY7#J8Xlr@$@=;G;M89oLE29VDg7?>Cs85kMG7#KmJ!NA18z`)GX%{YO9
zm4Stshv@<X149&qW?aHx#Iyrs3M+_!Vvq(XlOfbQ+>gP5LE-;@<^%r)7#LVZ7^-0|
zfvQme+YT~-nSp^phk=2Ci-Cdh4p@|dC4+$(%+g_ChKjQ=ZeU<w$Y5|_U}ex?P+-tz
zSirDUflEPOAzopP!d@jIWogwvYK&^X)H&7pA22`Qd?5Ki^MU09&j*nY5*}nesC>}*
zVERMOhmsF9A6h<id+7Nv@?qw~%7>i~r#_tdi0u*QBe6%4j}#xNKeBw}`6%*H!jJ#|
z|ASHlgBI8(1{^jqK45(y@Ic{#!2^c}0S^)$Bt0m2(C}cwL&k>!4;3C7Jal;I@i5?F
z!oz}x4G$+goc@U65$7X;M-q<|A1OaFc;xUX0Akbs$Nz8rPy3(rfaCs?`&;j4-jBS;
zdgt#g*;|q~6>kdP<WrDUkWmm*5K!P!V3GeP|3m(}{73oM@=xV!<jdp><g?^c<dfxN
z<-_Fz<-O%y<z3|N$=#8=CU;ftoZMNtqjLM@cFJw$5Cw-RGy-T$fpP}}QxDV@Q*cN!
zFerdh6$1l<76Su=HYoovFfc5DVo+LL%D}(?!t4;G3S6K(#lWDT55Ww=3=9hK5X>OL
zz@V@Of*C{^7!>wGFoPHagOU&gGl(-VC`&^yD3z=JVPF7Z21y16HAV<#kYZp^`vt)a
z(hLmhoDj?)!@!`<55Www3=9t#85kab(yAN-!vj_Zh6kJs3=E143=aet7#>J6Ffgbw
zFg#FTV0fU(z`$U@!0-TsEg2XXtQZ&`I503g@MK_MaA9D05Wv9jAd-QB!H<FAK>`Fb
zL@+QsNMc}kkjcQn5Xr#spn!qlK_vqNLm~sig9ZkM2b~NI4A~3}4<;}$JebbFz);G-
z@Q{&#;UUNkwG0do1sE6}g51!?!0=Fkf#D&@4HFm`9vUz(JOsI676ZdW2L^_RZVU_z
zOBfg)dN43N1i5E51H;1r28M?q_pD`Lc$mPz@DSvN?F<YL3m6z4g4}SBf#G2T1H(g*
z8%{GYJOr6Lm4SibDg(pA=?n}HXEHD_+-6{S#K6Gt2o#6+7#JRLLNLQq28Krh3=EIN
z7#J9yF)%!mU|@Iza_3tHhDVAJ%<!Ip;gK=}!y|PD28Is|437*L7#@M#{FQ;>kplz6
zBal1)GcY^~U|@I@$-uw}GCP5R;ZXtu1EVkl!;k+E%qYUZ_*D>$85l(w7{9FoV+KYs
z1~w*8IszqrP)z_$l3<DvT+V@*$i)|kkB%WaLGh0+!@>Y6#n44ZHlLjY?}O3|e&wS0
zr4h1>A_z7P7P#(aVEq4|fr){ML4$#TF@srwfti7wL2n}?gTBosb{2-WMjP2!@7Qc&
zW@312#ITVWB+LpD-o(zv_{U}wBS_p>P*G6TSWsDm(c#~V4<DEn{yflNy1>B5kiw9{
z;=|&@5Y52AXe1`jD90$vXl}<S&nU*m&uDBUCN3r_!p6_Y$EeLHF2~5v$fl;xC@R9n
z&ZexSrq8IyXv@gX$EeS!W@gW*tfZ!{W@=()&uGVJ&8V)&XwPUSCeLVUVrFi}6v3~?
z&CSI>k6nx-i=Bym-UBW{CN^d!MkYb72lLpO*t0mq*yr(cX(|z^j>U%p%~&k~MFBY9
zfWt$XfdS-wQ9(r`F<C|tHf2RY5jJf`B{fq)MI|*`MiVn*ML`oY#zQAWWf|i{Wo1SG
zEs_;IAu9WC5s1SWC(Gi4DF#viQVp@IfPsO<fyIZx6=D}VBOAM-kr*VfMG-8By|#>s
zN@|dxHHEX()EU*xtQn2u8O4n38O@k2e{r#L{F}nY%E-gT##X^5!BxvB3}f+gPvPeO
z_ZG&5v&vZ6{!QUv<@zNlw1h*1V~G%p4;RP~9v+Sb96Tvtave86KleH?6C%LF#+bvw
zCA5TtV~G%`)t17*%F@HKj=>s~j>N^pl}+@Rm6iCI*%jFp*+doD73ElrjqI3BjloGr
zU5`;++1%L7T-n^%j?vtV>BXl48@8>=CJvV3!rB%_2K<cGDgUmpF-}YQcZJiy(#b?r
z%+kR`d7WHR_VROODo#8%QwkVaZ>6+}*vXf&tlOKBsq1Z^$j>dO=Wmo^WR&7n;-t;b
zuVmn*y*j&ep24c}6n)c_@K)7cP|LBJfq}(`Wdnm9gF6EQqoR=<tEdd4s0bgcHX~95
z;0bm?6Ekr^V<R(D6E$VVHH>l2SxyFGJdBJyVg^oG&YPGR6`+CK#mzO9i~rwOD7O^B
z;@@`KL08v-WrLK8y^+6`3O6^midKM;gQ^rHeAjdH@pG+53UJ2TT>K~gU2zHvaR7z)
z3C0O5K1_QU_!$_4;Yk#dFn=*CfRf;!2cojFOnbnI5==0F{C<Lgf%yf?1_o6IM+OE^
zPSyqy?4WFK3`*&WrY2_A;5-Y;+sYsl#YIKL#N`-8*x30PWf>JsP1Mx&7){K~?HFwt
znP13?8YKq?CL4*${&|34pKvsnQBaUEcVwIZW3g<QCMuf}JyltGYV<T&(P`5}Wv4-S
zDYBx|PNdi*OYj)k81YCX+f18=#7RMGi!&H9FfjiA#Hhn^h{X}qequBd6E`z90u@xs
zib|jo$Q(p4e$U&gqoY@`YE{KwA^*ZqC#D-Lj^&AbYbz?(76rIw^YKW|ybcZ%28I-t
z4J_*!q!{EF7#KjM94PC9y=rF7$f&Mn$0#PxXl}-=sLssvO@WiEgYmMQh}`c0IT1O=
zEA3pI3MyRr1)NokmD>)pY!+4)*!@>bRa8q$RF&z~b^#R;PJt=Y1pdesFo7Bt;Qk88
zJ|6~d25|;u26IRZvNN(NfpW7rqnHRgBb%D3potkLqnVhfpa`2fqnZgjqZk{=d*+O0
zu<ULm$x_F~%JKIMhX{yZWoG2y;Q050gNuu~hmnJWiwnewVa(xR<@(*k#md2$1IpeI
z49eqwPjYZDnR9U6`uBrF1f-Ugi%E@(_1_0Z4h~kXzqdeII5<Qa{!Iay#hAmv0m|+i
zpjPq!5C1=~>|)u)z{$YN0P0JE+$6%!sBSLIsLsjAD$M*qjc+C|Z}PQxrN1+;F{S;J
z|F=1hWf$*EJ~h6WYYG2K{?59_l=dg9fYBcozbqSAHh|JIqaadB6l7Fn+VcyNxPJZP
zVcEdc_4k&nsF9JVER!0O8dxtM0|$!_%Nz!I1_nk|NQkks=`os_n~4ju3W6#tD96N%
zv4E*n-5}V_ze6S5-^1zeiU>z2Q<nMXVtzTLKL^bN#U(X!izFokm^f$uU8I#DDy5lM
zC?z4tRIdi=2{18m{{O+Un`JqJECZ<JY9z-b$}TPfiXVP(9I`2^vn%N_nb<M1DDg3h
z$T1q(F`B3`PB^ix*^iOYk5O2hQOsIOQ~2N8@;+9klx@v^e$9+U8>WPXO#u-s%l(?S
zr7*GfR{nb@r7B@1BEl%-%gE^0yzRuc=4gnTDI2DM>+T4a^(-!+{sE&MlN^(%2p==M
z9*e1o9g`d*Q>Q_7MQMbYb7jj4JG<qr70wn>WfhgoTU|U@b%V<sEGrx8H-(07s&A;Y
zbSMkfW%U4s<^RhJaV*s=Q$YO>Mqx7}aZusH&MGX%rmo7$re-dzs%B=)IK7!&l)aI0
zsVJM^zg11_{DS{h{#zx;&))QJsUVvOvjStHu<*b6f;>O}<%@9heEZJBEy6hAH;*6#
z6O#E#pcXL>8(5Xa+0Dh(&6U*&*v0ty!%?q;hrJHG^22RyO5daIo~J#RLFFc>8wW~f
zYM^=wQnQ1KH&##q2&&~l1)hnSxhkk|gRq$*YBt!Z@o+Mxaq{qkG7>+8t!B4@Wy6M=
zUwRqdT;gocSjD+wx%m0HV!6awpRtK^d1vVTssW7x@PaCPmJKXE4Ezkj49X1Z4B8C(
z48{!R4DJlx4E_wk4B?Ox0F=`~IUJPp#YK%xH5rxIL<L2}j1`T{Oa)EUj75!2Sw)Ra
z6_IFmMN~ED@}LreAL45M&=jV{e{X@@$fU-_&tIJKM<Rt;0ZKkZlKJx<A<p8%&-MEm
zNE3?`7k^4h3P0DsMIbI?9Ef`&B_$;VOrS}?gc-o)9jLwq`CpzvpTQo}x0;Mb;BpS;
zdqzc6KDxXS;{;F*_pbm{!{s7z{_aKMFd@r;TVo*gqDU&h?7u5yMZqS4dB|)A22hU`
z)$RTa@eKJ4^$h(C48q`I3f4?8F;gVKhg2%GQ*U$v6eu8qaVdxdH8y3Dn18-v6D36f
zC=lUhfQ?5b{uO{58aN3;^579b254S^q!mzjV}vv)JwO`ekSvNGvZTr*haA%$P$>R&
z5tWtw*9!M6<5D8Tu?G)!Ph%Ga6%~vOY79CoFIW^n<3XH|+8)#*0JRQG>>+$3#*0kW
z93t!+IXM1&WEW-s*TBKCkzIsiHM<z&T_$S?=g$WY4#ozEs@3caj0|}Uc`QyWP7I*F
zjkuTyJ13)<2)N4PWMos)XH+vW=VVj^TOkgzm$_pT2gkp!jNBX?n>a+-|9$!Qg<X_m
z69)$)_rI?YF-9In9(GX{>&<xOL3;juW#nOJU}g~i|C;3}%S;AI22}>oSP7{11l64o
zhcL1!W7eFk;CuxpK$%ArM1bNQ+&G1{RvBj=+1%vo$Ed@>k;^X1@$X7`U(%LFU*AT?
z{LM2WB4%u!84>Ys&onN6Z&2Cg&Ci8kvdr{t+MLu|`R^)+C`S$l2cx#HZ{rq_aS=12
zdX@RPP_#gGGobkgG;T<Yf6&_NM7cczRNg|%ULsuF4)!6K0HrA=25|;)mYLx8l`f>W
z394-%{aF(;R#Pz{Q4u~SB|Ron6Cra^K1NYUv&GazjY%y9lp#O_BmeSC(X+3;x;8ud
z((?40qN6X47S*t9KrS*LF0=c0b4Tg5+0oInua)j#RJU7Zexjn}Xi?G8l8O@yEDR|O
zp!^ML=Rxu}w3iKuQ&7zZ%FD=ZL+3+M14KQj8_vQw4HTsy;@>?G35pzk>`ayo(5jG&
zrH+gLUjaYYUm=iYrgvQY|E}<J{j&q{7!PsrpWx^E1!Di=;)jHv6oWL&G?r-$?hO8*
zmV-PKs7D|s&Mqz{&m<-)!q3FU&nV8uuFlTJ&%~yzq|c<L&&aN(ZmzDT&!lE*V$Wn|
z&!}!@E^cmS&txW~#3mwUWM;w?p{S|GYN*TFW~&{*EXl_>gOzm#BfkW5fPqCDtBw(?
znzlSlg^f-Cvm`ImbT+o>OuUlJ0s2<0th$D*YT6Z>>gqPtu}ou~!N@1c6kuT4#;Rk;
zrlu{Yq@l)UsKeT3s}sm9$;ULEm32B3pCof2NQJHeo0^t_qJ|oq0mycp04A{Q|7O;0
ztgG9|zz&HgRuKka25|;O22}=423>H+3!HW#gehnY#MH!0TvXANQ4rF2G8GgTG)94p
zUl?^Xtr>;6_<wKV=VBDL)@0OS)Unq5>!N85YCM22tBAE`3O|>(Hy3{jSoGhOKMypm
ztu>hyQvSLy?Lo#2j0`*s46Gb17eVcPVMa3}aYivVZIA;X$r&+FqpYMR#FWD!%FdL)
z!OFEB9HpRW{>8<=o{N<!iz$Jf{qF(}uD=ICQO#KXuM<Qw*>f>4f&35dCkui~VMcH(
z4DLk}Gh@(Tmn|bx1jskCqTsH!D09#+QCXG^{|Yp%|CWLB42zGbEU29S1u7@N`EMoz
zKZ7)Ayq&>FjtP?C(7Fkr<Sh#60723?I8QtV<vD29i=NH6{{<*Rfrx)KMj+4dgZ;+O
zG83)_luNC_ITS?j@^k%r%g<#5@*k)m0jKHzb3k)lEE^cOLA@?bMrLJZMMh(0V`gJU
zW@Bbnk-yJ0KWYAb#<>2X=pvB||K>?QkY=3luYhI4zblM7{|Z1P*c?z;f%|Tv4EnIL
z#n?y;)}gg#1dWq{`sYXiXw3*4Y+zD5Au79Hgq!!@PF^(-!7Z|1mMH}6R7k_qh>M@O
zg&$nb*oh1LD-aN1oFE|1cnH)fgqDb0AW!jgtzXZ@j~3R33=D+Bnw^<VNu3!qk_K`r
zp1_{Q#osB)#e1JujrTq;muM$}5EmEt_f|lFQCL76l>R~eW@tGKNeB4crYek*sFjt}
z%!SR27;(GpImAJr(hurHoT-8F-&+Cke{aEwL0BB*K1e*m`bLrrmXJ2OxG=a`&JG@U
zP**b*HZd2rV`NoSVq<4!69dH|Xu#N-(Fi)`Yh<R*tjG9}=^d9a^DcG{RvyXUTX^J{
z*cjP&G7B?J|9gc?h$)<jhie*_Fq0aSFxM|AJ&lWpDV#}&Wdk4A-x3MQf8iia94x$&
zj4LH2n0mPQPVjO6v*YID<C?(*CAb+6ar2?YHE2!-XTA~#w{^h7DtJP%`>&wjUjYdT
zoM9Kck4J@P9}hREv%t;32#wDT;IVmg$k+p@7h}(;rVQ`=n;OHiI+L*x^X7JTZho$R
z)3{mKTXP%1RpX1JMbi*DxAXvuN;^9X_rGae{M_uVKA;Q?sz0H%DyS%c#Fsb&Be+hN
zVlZW3V8patRN2Ii$=FC3WWBPIIy+=g$rwDcEM{zE#|Uu?_rK}fEbMK0Ah&>=QF`Dt
zgqqXF&dtxwn8C%$(VEv(a<mBQuIb$T-0W@MMMq0OPWrnaM3%L2uyQeGfMh@}2G5YA
z*Y%*bJf!b5ACkU6^*kuFKxG^#RU!9wK?NbGjSni+LG?Vy*&sehJ&4Z^ZxDjof}pAh
zRH8s?A!^9ig8IAAzAy8EKkGobg*k|epZ_lhxNHIumq1MgXtQA-NB~^Ik}HbZIyu45
zg|IXQT6V+j09kZ`GC@ch0_AzoShys(S72(QrmU#MCd;UdI;@}!85RbGFmp?3@V_fT
zWhvnLxdlZ2T_GoOLPU;P;Y4t0S<ne<&A%%^!b}mG*3&@s;50~m3Z7Tt1dn%uYXfCL
z6EjvLaZnM%&d02*q{pbP$E2(Tnp#p<GZh5Q3&=5x%P|=nnVZ!baq*|{Gaq1K6<}6$
zb}*EYve#1A)Nz*!^=9W_W91X8uol<Q6Sid0)OD3j4`2rmTbyMv=HfSCViWuq&BkUO
zpu)?`$H&jlZ^_Ec&d<2bJe-+{nVU_7k&llroCDOB2ZhB3mgx+N42Gc5aZtShN}-?>
zXewx;$Ed)^h{U&JWCnW!QB8ve0FV@zF+O>5Z*ej>mU^H3`}L$3%uHUawSWUMx5C4e
z0B5pHPhNbl<lhxgI%CwSzgSdsu^z@NxwrW50UnNjCLn!`XFw!^4U5OiEbh#A8AL&4
zH*6GCT~!V0c+eyft2ub&ScIPuQWh%dGqOYaefEs%(0(7IBO?<h4-e<Rf1;9*2CyVk
zL<PGDyAnHlMFl&%61xb92J_3yA$(@NHEislmNYxt8a{q-RosF@X$9CQ4$u%XcrN=g
za}n6hpmqav<QCP<s>a|MNOm@UMkUa6qq&)>i8^Sq(F}`A885MLu_Z*YaWiwW%d#`G
zdHAt0bI7t|abF4}XXC<I^_+~1Y~t*~9O-o}SsWtlV&JK9(5(3pmL%pk4AKmS4Bns?
z4cHBA>}>4Ha7U;?T)|4BtC^28bF;O(IIwdubFyo3FtO!Zo3k--YO()q!U%X=-g(2w
zx%p=1=@3w_olS&e+NP*A?wlfQimdxE;sKjKMcE+!gpEsXVBlmBWsqc$1-J8c8LS!X
z8JwYGlMIZ;qROI*qRN7bjLM?MqKbmbg36|ftb&SQ$f|5=48n|{>1AV6W3XyvQ)N?Q
zQ>ZF-m<kZBXsXEgB_-uwK?>s+radQ4{JU}@g(>3q7DgS$35+^_9;Bo&?MX@b>vG~B
z&xsVqFDF0)izgU${<<*gq@<)Ux1^-}<4H+j`~qruf_N!R5&w8nPJsFoCsIIyAPgFe
z{KxZ;hw)3wzbl~5#fg*?aB+|TD8E3)^+4mmpt*EV`xn&Dk!Fwwm4fEVg2uwig2s$U
zG^3)aqPViDF$jZv3>n^JoN(gAiIfyZof9YiJb+pP<)2`F@kas`{|-n=i3S1CJRAc9
z7t1o14Gg@X)j*(u7)Wzj*u>13MGewDWc<Rkii6{y0cdt7jFF#<(b+oK+B(>pWy9Yv
z(0q*n2M5zC(6r!LYwHkeYmmP{X_t%j64+cx(0U(KlNBLzOG^5ToQ$|^VP=EdYQebT
zugky7IBbPG!h&%X(;kEy7~%Z^&=|X-k*FYOYzNfv0@W@2jBKC*ZBU^PZdIBJf(D!z
zb(k%^g&a7z___YNND4V{uyQf&0hNTHDp06fh-HKKpBjib^ENJih{``+AQjB#C55_$
zKyw!iC;tBj&+Br*S{Iz4t_OHJ^q<{7J6TW{;&&6{o`23P^`f${@Pdp-h%&e`go0*+
z!EIYmvlcY$ZqEpA(SoW&(6l_L?Exx<w4o#Opq4MFtpRPDfLg@hk$Of&B{rx>%|Pmz
z&HlM?3o!bz^I5Wqb1@#`5@)mHV-IE&U<{kaCC1{%A_kgmgD~Jj7+j!H3GfIG|G$m@
zc)(3%ZZ4(>E^biU8Pr(#cUeL-2UJ?;h)OW(f(A5rIp=Wlf(d@Ezh$7{K{Bq3i~sKm
zL=zh1e-;MF+@}vX_iKWC_{K(ZO!BZ%7m$xZtut#zI~GP!V^bDL(lk*q5))xlQZr%v
za>63kM1`Gg8mPwqBLO0(F>$IJ$1<Ks`FDO_d;7ljfA7O=Y{G0<d^D}CMMaQX2OQF_
zrrMxf>edcY$zcOh2yzz_s6`8&lT`raNKQu31hhP(sG_NnnLeW;8#|jEld-6xshPPQ
zqdg-dvm!IoO2$SJ4Ix!du7B~SI)Xv}Dy@{bIkk-B{!O-!;gMozXY60Y7_x|E{U0eK
zEn!hcCMTzG-Fp)wmGoqIoSY<NcvN|Lncw}5Vmt}5pBa>fnLn^>U{Gf;0<US1V*|~u
zDJz=VF@o9xEb6AL;GsS~CU#ZuN&r13ZP3IeXib1NBhyD#VN(NZEm`x3+#G{{Jet;w
zY~13CQjFIvOOx&7b#(>)^>_Pa3Tg%0mE8yks*Rh^)X2y#A!}?Eq~~1fXD$`VZmhv<
zEg>SqEx^g{&Zy~G>}(uurOj!W=%s0GBK>DwYCsfdnJ)uqK0lvj19%QloR1OIF9VIo
zn3)Tkndq^otErnZ=Q0I5vA1yQc>Uwq#kZE3nV&~qRGgQMWrL-#sm7n3h8k>%i2^~)
z4uVp`0-W4z?95D{1#JvW|F=TN2UNlRdSw$mR?sku7$hF-m`w$hmH1debpxxipfS6;
zv7oX!tB4T?x1_L|gyab$Ny&c&EPP5*Vkaae)PyCuIgFTJ{GRRYU0dtzUCZ1esvye8
z!OZw)otPveQ%VXWE2k(Qa}Xml2cM{dD5K7aKM$5KXI5Cwz{H^Ve=AErc+CQ+oq)$3
z!tADsqUz?Nil!_+_}$^W9AZ4&8Gp-=oWaxucgF7~kZWtby`l41ka;}l*d=Ik6uD1A
zD&L4H0@ObKy8=WqzW|YcBtRt63Jy`wD3Yw`?=7OT;3Y+%fmqQLQQ3c2WI+^|z{MuU
z&CKAzJVd_<-4E#UzmO(EHY2(1E)H?fqzI*MW&{s4f_fhiccSx!3CJ^kkrhP*HE2i<
z5!feWMG>J53P^;Xn76?kF8l8#0VZsole*~x;TBosbbyOZa#({K;u!O7pguay=iERw
z3@Gn_Iw+tz6n)Ol2p-^!j)(|CjtGjxk%J1Hhrk3V{^9mw&0b(-U;?{Z1_n0JxGFeI
znZfh@S`1bUUJMM1%7WHRkR%NAGI~~HWd}E%SqbDw#tA7Y(-5v?`~oU5{_%h&EPpLO
zaf0y>qYhZ)3W!7`dz_`muUhXE@AY6mfeB=fL3d>^u!_LfFF^f+KKl)7EraImL2(Ld
zHGtxj71VoBHz(vFL{u}Ur=<K@hvXv`{SzmaFaQ0F2rvEK!s3JM^b;xGwY9jt1@3>I
zVA;U@f`Okw2-aeRlrNwb4=0m1(>r+`9r?dP@;W*!EuiK-^9vpMzhxjnrY?DKzl0IA
z=L0fs;KRVrAP%0BKwG^aE@;lG&L}L%0&O$tutW$+NeTU4AtWW`^_@}6i&6E>pGyUP
zeg%FkK2kz|ctEnuUxcJm{>pD_p2KMO?*ZeN62E+Z|9nu}go)uc11pOe%R~k-(0VmS
zBRNJ<5q3UCenwDz4_f>q25qD<b(9_}c5~fiX2z))7m=%J<5E0-`}U@$?M*BbD^8Ub
z_vgN}x8qPu%b4NOQW~+i(&KQ`j>bmNUK!YYA!wXh9Bl-hQ4lh)i9U+z4Iauw8#w)w
z&m0CGzXbJpN*S1$@3ZnVK*lQ}Eh=_BW>XV8R%SjXaXA)oF*AEcGc|QRCUrG)Gi5a<
zEm2SZa6da$<FJBS`;gf+UMluJ;Q^j|#5FaT8Ko5sWrRh=nAn&&Ia!4?d88DWI5=4O
zJ^D+_i%Z*G-5cWE)L2-plBY+ywwD!Gluh{iPtm){O4U(anU8~=M?i^7f}KUcQ8RKI
zH)!22sPDwUa+)QFfrmj0+~P3@t)l|1dS+5pG!kb=T54m<XpS_Q!O6&O&dB^Chlx!@
zRF+SSi<K?sFDv5&BlF$=CV(2mj9)aZPwZa&uVCrUoy->hrY$sQOlJOC%*e#V!p|qB
z#m!ZGf_JyICa9Tct-0;Qzb#95?p(?^Vd>6)*Z)m3TE&<QPOA(olUaNiq#4XWy%f+;
z1Y&WJxhNvtvO@ZJYU-wHe9Y{2Z04qRjEr%NnM+I#!`-rTDcCJc^^CzB9RJp^b2Ca=
zri4UDap_rlI=E>GGjg-|OfrMHXl6RhJ<|U!gBBZ{V`Jew&B(+nW}s-D<Y*!*!p?Y!
z3p{oL-j~2Kmj%?H0F6mw1{;z~L2C@;A+80lK7g-nh+~Yiao+TAF}iyhPce40i?XM(
zv;Td|F3SFcojsLZls%tAgvCdEBh0m~CvL->`_GuM8=~g#8+P^|5Uu&3)&7hOT;Trq
zGEmzd<POLjqNp)wIuqg~HCxb#2l6zQsj?_!DW*B2nV2l2v8l3>8lxQ}2Zsm;BMUn#
z*Qyg-9IzFbCsuK>vj6)7>Zbny%{`s~uf+sObDdbv#mYPlu5c|E3;Vx6pkV#Z&cd}8
zSs_S@k&|7Nosk71#lQ$|@3*k{fZF?@5?E9azC;whKHbFZSBj`C;{|?EQT~4h;36Gd
z_W$!@Jj%x*`bR>PgOBm(KQB=A0BQ$;`_8a3Oc}DS32H8~;p&JfRueN&kqxo|gh94|
zFosPS7Gkk?E`ub?1{Pz`C<w@WuqB}7uAndmJA!2c$ZS#me<J++jBofwK{m2TAw0ps
z$M}Fx3q){;LM#OP=LCZoi!qBC12+SMFf>9zrTS-PJt3h#mxLrGS&aViam#W4<KyNN
zWqu*b$H2sJf+2;u1zaD<!$Ls>y5=9Ux(j)5_ZQOIAUG4W92z<qj97yWZeN1d9-aX0
zwE&yX0P-uSmIS%USkXuf)Eq%*WZs5ggU2aA<&z|fFf5!*%n+-Dp-Y9u8NY~v!Uq&i
zqW=s)anF>(xJ{IU@1Fvn7Kq>wW!(19nvVnITMj<Z8tQ)xOe~vO{6RfzMsp)EQDG4_
zWp*VsQ)ZBv5CUWqGn8PFND+}^1;;YeKSMrIrhi8KqO79)jF*_ege;36qbw+S$ui1{
zGH(56#m@<%EcrM^m|uu;^8d4A+$O@w_s^Db8))=}kx>V{79E_HL9IbV=z{DAt+59U
z-!KQknmQ0)F<t;CW=KB?*+>7ZA?AYS?oY66VDVt!2JMjJL|QkE-acVk$M}_>Q|$LU
zF;0HQul!nnzk~7v#1FD8mVck{i;3|w$?*U60Q(wDFfcN7{QtvT%HjpuNyW*;&dJDJ
z`fmYa!qLAsm^7GM{>}e4{}_`7cxf^NBct{IKP(zhwV;*J<}4b2ZysYz1SwJmvltVP
zF(B8s#%T2|t1@&#MwU@T%$iXYL>QUbG8%&j6E$r{Qxi4xy0|t)R2DMz0(LW$iB>o7
zk`<kXEDdV!P6NdZsIG>r7XsD)@@Vb>4Q_#)0vaE)1ubkwos(vA1vf=S!HqaH8~@dT
z?E@2d{mi1o&#11Z#LvhkBG1SUB0&3F#6g6ai9Vw_h(P!AQ%TN>3QkGRUQQ6r*~^K>
zM{~<s?5g0pP~4}29tLp7i5ZzQf?T7f#E$MD(GYe~j&64LPoNUKoShw1ccD4rpJ6CF
zdpCy&$43qh#&U>qP#cdKI<E{0e{0Yj1ZXY_Hi^v0s>H`EDq_tjW^M=0HzI88N_?#B
z;2C*4W>XVAW?^uefu$78^kFY+<7A{L(k#cyCMz!@&L_+$$tNWw%)`XU%eR(YPD5Ej
z$=Jol3O;y<=In{wa{B&8lB@zE0-{3VV(c93d?GShk{p5p8Wu5T`W{-+oPQrfhAu(t
z5kPZm$mz)rEi@r19N}7Kxa&osELd98W)#Hq%rv+&A#1ik%ZT{F(@>ygduUD%LpTex
z6vzm?)C)GP3SOlFX~Tf#2J{)?L9;)gPMaK~IDEdDkC7d|1`)K62{Qj|&1lDHZfasC
z4430$M9fjkF^YpX)_}~iV>E{^UDRV_;+@9DpFUms5NNdako5F)ume#9c7lam(!^JQ
z)T|Isb3qpXjRs@a3krXTVgJtJ)DANbZ9RoOT09|dXJ8ixt)~DJpgBbJiA2zL89b|1
zW;w94*VM4HJFts_Xi;`FK3b$97BWTQ(g8IAV~vdg0Y8AIT|i4;A&pOSd`oPav)I}H
z9pDh*IK{z{#V*S3f0SJmr$cK&D*o+fXFts@%AUo+;eQm=?nKVl5}^4R^nA@IDk9FP
zCe8?6o?&W&UZjBfSdL8InP30z$S4La$iP_eRZ@}3<n4Ij?+&}7qb0?lvIMk7C75Lc
z^G{G4OBmdugAXu3_ZF!$aS4bsseu}Ve{YEk{QV}t<i@y~mrL~DUGQ4&e|JT>co|po
zg0|a$HYPI!v&@8;2c2I++ZD-F&%`AFGDiIGEs&v1YU0cv|Am6qcZKqDi83mKO=VOT
z1?_ZZWZ2HYz!J+c6XZTfZ4GbADk`ag_B%1H!s;59&cELT5LO5<ae;Osfy@(UiDlUU
zG7nrng7$EN;u*BP3cPC*yygsKJuix*K=VUT|AWi|hX|<M#=x+hWhP53XeA)Rzo5<)
zi@BhQ8N#!18<sYzu<As&AFmT<2}L%VNyVkqH*@3MLN|6sHc`;}LP&cX<VI~!e-5(W
z0cAqo47A}B)IJ2~B~V8RG;z<y90VE*5#@{f$sxj##m@dSj*sJ?;=e1RvZ!Jp&-}Y0
z%E1@+lbt<_LxkgJ9G~d#09jE+9W+7k9ym`1PnL9+bOuq-Ne7Sx0^*PjXK*KgT2G)h
z8mcRqzd;=#ig3eU9!xi~B)}YjaKhh@vLIckZUm)S<a`1eYe!wf2dXc`h2<DQ({JX&
zW@^mPS^~t!(jsBpd_hEl#fn8)#7sn)#fnA35ME)RW#3)0BL6Ol$jXW^>WRqy;{lhl
zU;^YOa0eH>&e4-0lp&EJgCU;*vS(UMo>5F(%p5d!VGi1eD9<P+&jfZJc!LpWwosN4
zG<6QzI|&>713A_dH1}z4#{#Xp!E=SI>T2M*Ly){MXgi~zCZw|<$jHJe!pbTpc0`n4
zmV;aEh?uybsJH<yFCU8}2O~e1EvRxZ0?h)R0Ih5W*J-xge4LW3d`xVjV)9ILm$9>O
z-R9zAHf9$1XS$I?fZ3Rti|aWzvm9vQE8_&F2!5`Aoh%zzn3%YD%Xqm(IeEp(c)8dW
z1qDPUs&zQI!OK_pxz;o4{JXLqY8x9Xi$Q~=2s4M6AYbJob}l}ysa!0p=CZSJ^>guY
z{VC$-f~>1$oB#?#P~FPIvYurbgEnaOKd3NbW8!1cV^mjTSK|Y3FE=$Yw*&1!SJwko
zsCI1PYM_0m{EU1o?0k%3X6ANG=61~Hc8n~&98yfWPC1F)NqsIBwt_5z#?mvT#I!g<
zC4?22Ihh2k4Ap}YW7?|^sB*~4t1Ys%vxq5)3=)^%6Vg?b>?(|QOU#be6k}mo#w^US
z>EGQGQzHxoIk>rb7#W$QrDsYAD><lfc!>(y$eYL4x7r(fnW`#F>$`ADsOc9fD$1D!
z`KOy&*s!Q;$VEFCsyf8ihZ>16Ffzn4$gvc$oM!;766a%-V-eS5w_`MyV`gLLV_|0l
z4QrX(f&44R#|+v=tFC6pV$LQ9@{k^rx|$s`Q?I%_m%4?!lLCu0mjsspGb5)EUqY9M
z0-FG@oma4(n1{K%5F0nIy0#Lhn4cq;sHtysf_^|c6B7%on4+}?2b-+Ak%T<IgpwLN
zzmjKCB+Gf{hSV4rOBEh=3vmlMGi5_nO(Cu3iOZ*ZnWwv3DX@ugv+**suqs%%$0U|o
zW~|EhmSq=JnG-8+YGPyKs$`lRXd%YQtLNYtsu(&mBM`LZk%9UD9fmBHD=cmd(hT+t
zf#5opRfLaKNsraUj)hf?4SZ|>XfLk0nHoDN0>$N6jO>`r&D7M@SlRg)#l>J_eV|Za
zVp}q|I?vxyM#joNuX^qx5XVYJ#?n8pnu(vEjYD$7j}77+?0jxfye!;eMp5z29nJAk
zMq=D7JW+rDEAw-5s#*x>@+tE%^XuqxE3vrgdd4Khcxr)3U1ZK3el}rCyP6FfYOKwL
z+4x<H1q_8$+~Qme4P4_~RfG%$OXXDf^@Usvcv<+B_*MCJJcU4W<ShU1Ffg!OVcEbS
z!l2Gz&ENwX&o>8ewPZCm5;r#%HV2Ku@-ef65--S;s!&gho9i)}nwXoZiNk%(qz?9T
zz!drDm1U7SU~kS1vUOCLqNbnIsUDCuBS_D;OpTj`$Lwz)*vmp%9zGeSn%Vv09PBl4
z@A8N-gM3>dl2}reBrOW@EPv&{psk_TOr)ZU@{*<28g|$V849`;fjuf`6X<BkJ4<MJ
z9?0_raF23_u|WOF{C^$;1FI0rY6flw3DBBWbw)EW(AXGgtU?vB2v8B+v;{4R02ODT
z;~%1!nnWx_n3_bTReya_m1g8(j0BgQjFJDGGtkQ46c#OcR>j{B6x>}}Ap3EcF5j0G
z`2#9>|A>GFJV0wmM8RdP1}JTbi3)-brVwFMR}}=8sh~OtbP9vHAS)Y)HZx}wG-2|o
ziD1m;7n2bDH#0>_`0QC>sg!>+1trAz8M9e7MA-dX`)@a&p@^mkFVD|^KVwB4L<Kk-
z8aM?+9YkUo*?;ozifD=$@-ezH27`Jif}jEmyq-iBY0oC|>L}38&1i;rhGbYT0o+A^
zgc7{958^@!aM0Ni;-bc;pnaUk7_?^-nGa%P69bQg7>UaM-Xbe%1nxsKevxJ5hYVMM
zRyslOpBf0Al9GbN1T_bYAiM63Kut2x2I_wWe}zD7rgxx4Q&3&lnc#Lc1A`=MIm>kL
z-Ys(m21qLb6#UAdrV41n$C{B5G=$2?t`6<t7%L*FQa2X|$+G1C<mdm%Cp!P{ihp-O
z_FrU*_$c~!WpC9*5RXxrSqh@y<JR7)`Jybn&-k?Xp7C*rP6KV#Oc9l3ikK$K@oz?N
z6*v(7`NHM5_Et?31<h%K#>+tKdVEl4b{GYfL6Kl$mcsl3G`ReC&A$sNn%34VKGxth
ztDrd^YfaF)Rj_|S>z6@mV>uaw89=*M;n53P=m|L<fKd=q>X`~Eii(KJFv_tR*|Dja
zn%FW<1DyqOMV9Fzgl6PtlhE)<n{!}JnvaGA8>0?0JFkemx|x%iy1XzyD=Q-?f`0u1
z1ptTyZLiSu%nFOK)6lSs3Cr@-WYqb^DI}_`uBs?4CcwcdATFb*s;(s}$ngs_-ueGO
z!xa`E=0l({DMofqMs-FOpJSk*sH05kjI)n}#+!~ase{|@?^t}9?|{^CGMRHSviSbJ
z1s#TB7X5pZN&Wc0`HYF6q|fv}kRgRNp7{lX2xy->r1fVEI-dm8;RA1$P*fEM?@~|{
z2RBrdK?jtW88J>_dIcJO;^2cv%)jP;GT<0wY`hLibJIk@lAz(zzc)qsAkob@amP<S
z(SHReWJQ_OK!TvQHyZ;3%RH71pgl97@ossLGuWB=7?l;p6+!c9;NvSmJ#a-)(1Ije
z(8e5nMrLItH4`(7I6)Z}HRc8hIo1?b9mT)x22A3h05+1<0xh4=l9d;dWn(>QCL$-t
zvO!E>k*dT$RZ;B|vRbn6EFvpvB*MwXd{9;kR4hT~VL)^Ipf!UGpan_BkfrCMtcrq)
zEE|3YoUqmeZFu_Y0^%^gNCBlU%@cnfK=iWSVew($0iBp2%q$4gswm3JsK|KY1X!z<
z72_9B1M7DHqYksezXDKdwAMV40@?$`z{u#zz`&f%vK}<D$p{I2)F^?*0aG<NxS`<;
z$vsdG%YvVLqJNiz0vMDFnIb_fP#R)jWOQL!&zuc48@y`ImJt-L&<?T~B+WCg_{qoj
zlV9}j3Q>Ltizz~sIpHb4CjV1@PLb);ML7AP9MP#$ML}yVL3Jo7KZ4epLROkGFf!{g
zg4UX|GlN$CBM&IDLQ6=NiECI@ME)j-sIabK(S@un1Lxa+t5Z@^7$-2@5!cla{VSVd
ztqBg~6isVz;RVXO|L1_u^Z=c)p$uBp#sKMHDS?(DGOH>Ji?JyyfvPTbCR;`mQxnjE
zMXHRVN_xz;j3#zW<}8_7va(vTDSu<t^qB=kS(!paWLeCaxOq-6E&d|`UeqKiE6WWY
zwO<EHQp`c3e`cQe_eOw2MwBs-?-vt`06*`qe^-7natmw97)fb}@PNuBCeTU%mNb?P
z3_73{ZlK;NKcgI@xG-pR33R{;i?Wg)3uwt8XkRO6vyHjBni;d0v5`3+qmmkvW}Ju+
zn^15SzkQjYu#A)thg?e5e1FT5m{2=?9&RBi8DYUPd;Y3mAvR$ZBNJ0Y9&<MfmJOo9
zs>1cY|8}mG)zn~T;g%5-;^Gq&;$vmu(9o1!%joV~C!!!M^?M&1o1v{O10#b7g9ytU
zmN}sQE~7cSI0&k<gP^%O2#TAFn=>ms@O!mo%PYSJy0d5NBC(k_VF*E_L8nfF<75Ma
zB7-i2Ev(K2owy^*C<4mUpa{_iWnj>vP$oM@aehWYV{v0)Q$=%AP*8z7pCC1+f+l86
zT}B!<GK?M?HnQ&obi{wLyUEMj>Y9s8OyD(?PMMa{aqZVXJEOm4u*m~imJNYA(`o~C
zQgmhit&mVOl9W_YXPlrSedL&oM#`0||2k6`%WEI52dyAL3}A!GLCCx?D9<Y}7%<pC
z+$}CF23hC;ra+DYZSxca#hMvt=L)h@71@;q<r$4l)fG*dnIe9P>j=D)wbA&uL&iqK
zNZOD$VWNn+u5Ha<#0n#(_>PomDMm(&hkmj6C`$ZWA*-9B6IjMRO(#%8=GYNw6~+nb
zDk&+lq6mkk{Oi1WW&Oii#&SqFv+}X{Gf0Eam;jxL0$R_?4=OrA^I7216Py#w%*6%8
z6@@{m-^ff&Sy77_w1mK(Q61e2j46!S@?v?sj65NXdLcP^Az96A()|Bcu`iS1vr1u9
z1@TUc3JZz-vtZ(4;$&uLW@TpN5u6nE*B>4M%$d&w#0q%T_$z%T`2AZes<DWdiC0jS
z%i|wsBxA6shLI5?6B9Ellcd=8;3vp_L(0$o46+Pn3~mezjI44jpfCotOVuHzy|E%>
zNj|%{7&{-U7L&4~DCqn$VMTL6RSf^!X3S&a5E0;A!~Sm-zcgEOR%mWc2%}yoHzRMJ
znAaph9wug1W_D&yW-i8ms(1Ni*qRvCvi_}N3V?fu#s3*2tAL<5_utRFi!?+TgZ(D>
zRPw9w7Kp9eE+)yu%FM*Xn4%&2Z*3&w_cs{+0iBh|;={@Ziz5cmz%Z*Ecz+4VPx_3^
z;KRP;83j$mO-<C;L<MD-`B>Q5^cmT~KGI`EUzo>uZ-O5v92fEa{Vb@;#R&`TW5@yf
zhVgqODD0<!r)MAuh?Nf|KKYQN`Wn_){kIkp=YP4uEif>_!2JI&0~_-TRuKk%23>IP
zUJ)`w1uEr1<vZx0DNwRw2c03OW-e^U0-E^*#UeW+v$(mLxE!kt6KD&V8K@~~t}3X`
z7>DFcMt*n*3qXUOi5C>~e>>j<!-L$&2ox8QppqFDRezpkh2()^H-rZiyYh_LOdP`e
zJj)<a&v^dd+OSE2JdBVCXX0Y|XCWpeEP5K0EErW&7?>Eu7{pj+vCLxNXHW(8Mv!*y
zGODVv$s#7iL`B5RRn1HoAH9KWfQL{_Z5aD4S!PAgz6Oy6QU5G4HgAIF;Bec^%g81o
z&MIbR%Lor+Qxi2bdl^-ccb|fG0E?s9`vS3%6*N!K2_BEd>tALyCD0OMYtW8p&<K?&
z=omCKdzrj3j778ep97<?0BCP4ViziY|FSBB_M<5)v55<d8G(lUAQM5T;mkN4b9*jo
zIPV4r0?1;}rdQ~uTxJFd1__oKEHfDR8KyBTLyKWmK4x|{HFiEmb~Z66DI&&hB*!Q&
z$0RPtWG==BW{I$gh>L;7ra+t67}b^dm_UO)W~O?K>SlV3Y#@%gnK?)cvpl06XrzY`
zG>oNYZX{-8X0FGqZequzrY;9MGEt97osC^Yj!{&PN!^aoT+GOhk@3lktFvQbW?zL+
zj4AxuGFDvtw)`UY%*?`!%!aI-GHgnWJYtgU0!sSaqV|@u>>`rPyduiN?5r83a*8U<
zyaqh{u7W~(8ae{T{49)Ynk<aW+)_f~!ou8a0&E)6Z0us9!fdQe+^o#Zyy7fuoa_=}
zYyun#g5sQ9T8fU`EKH2d%<|^kJgl-h7Q8}=0zw@2S^~^c%v?fTf-E!OZUZ?{kVlZ6
zlZly)m0g{mTbx~~NKC0rhSObwON51kPk@C}PmYCOO-oQzT84#Ph>M4*NLIy8frF8q
zU4WaBiJMzloY9bxmy?NKUXn>fQdUMzfQ^xnor_08ke7v-M^!;UNKBlUPfJ9SOGt>B
zlUrO}M2U@&kCTU;5j2h^qrxM^&5|x?q`}O{$<58J%Fe|D?#hGPUo7icTES<=nTfNC
zn~Q-4Y1CndpD?PKF@0Im_wVn--)sTCHf`2_*YIoe=kxP3MJV~Rw8o@%y{L<9*H+>d
z`@2R!Adg>LfGIrnB>2pl1eOgfYvAWQnS)jpVJz-c7Bxm+2+67_s)$Vt+C2p~d07Nc
zoB-`40`*s+0{?gzzn}|UVboz+gJ=k+q@2LX0gv~?`nKE*f{?Y3kQHPmW{iUDf{cpO
zm|H;2+CMd*6PBz`{9DA_!m>dY+$jF*^6$zC#xIPZ6Tv}kBhXoP8yLhHq(P-4sF@}U
zYG{ZGDw-;bf{w^E291D##yvpgKd5fAWn}zvLem=5OZvxi;vWyF!(^>_g7J&BCdh*<
z8>}@`tTq1?fZF()){p}s{<@q11;7c=v<awD#sZq31<h4B!cNcPhYmD=cQt~V`DUOM
z#h}^+G*$#%V5Y8S22qASx4~+TymuS68`20eS8^OOfpR>KPgIuK6gstW95VfK95nr+
zpT*7&Vt^(yK+`bCIYgLFgH(eAIQZhA2C!@p<%>ho2bzij9UKatrGcseS@Ks6G*tp(
z$%2%C4LBFa#{n`8YCLG>2evksWd+L$@VOGo4Au<J7^?+XAj>tuhtR2jy39&yknKCl
zO6u&OLlZ&6v}X38Q%YbfRAI^(Z-2TwKR$l`-A{KH#KkROJoaFJe*S(C@lRJ*K|xo6
z(MMN7L5z>zT+rCVSW~`=k@H_WSb|9wOtGwhYrgyG?)-3w=KT-$vnl8*DuR?Ka!nMM
zQ86}FQ7~k!W8~u#U!njK1{toP%fR^m2169fPL@3k(F_c#pcDPn)Il5Tnb{fG`B=o|
z7>z;8FVyuILEDDe8TmkG7%_lXMTpCRy2tE%;NwO?n>Ecrx{ZyX;*5Nt18SL+7_UZi
zu$RS)Nh&!S*efdh>)@A@(-r1r<%~4<GG<ay5nz`Ul9J$+`nQ^2Q-+VxT}jzdkB?u+
zmX}XLnMb2MuRA-8&%(j2OvymXh4H_;Ms#+7hhI9spoP;vIeskx0p_)g*P>ICW(%^2
zNEs>0|Lf$JmNnHdm$h>OnXWD%BF-cAZyi6Qj3ytWuadGNBZs-BezG#RRz+TCnh(3Q
zgQed-7sme{66T63ZQ-$7o&KrtGx76-CSpPBoM7kwxiBzbo&X41CZYs7g_TWIM2<;Z
z3^oLBZpQ*T*9ZIgfQ-tD_C~r|it3uW_DatA(vC*P{OtTz+J=g}O2(QFJS-gAn)b5d
zS$NJ7)KxPxRZ}xHQ_J@ix9~DC<Y(uz(bAKUFwk(|X5rM-a?vsp_RnQtVzB)GiKU!n
zAA>l99;nupX9T52envHYMssyWadyzCgE=F!IcRzfd;k_mz#eoIC%ZT!)9T$$y-pTN
zvhsRD@!$VdU&$2ib#h{q7Ruz}>~(Vb_ta^ZQ?HYgxhzPUv6W??Q?FB;R<@F?sh&{$
zkAKxyvV?k_b~!Oh|8vXa-~=)MJ#}*Gb=vJ@ZmusF|AVm=RLn4g*IR<lOBG}g1)Zx7
zny&|yDWG!9RM5nZkzG(xkO8*dnr+pr6f4M3qD`6|Q^ddjVYaqmwk#Xu^o&IRxPwYC
z=AEL(x~o8gIe*1$L9*a6Imp<E7y|=nei<~p#we)H$STaJt_a$hZDz#W#wg7%tHr1C
z@8y+$Xa2o7&1igyQHPORnO|0mW#PYP{93a7%KyIpyK?E@+0%>?j3!qY#Z~wqV^++J
z>I{M`oGcF*j2Ik1r30g=9E-TJ9<#cjxEu@UEN4+=Q_zfry0ILixS%?CR)fjhRFV<2
z%1Dk`TufZhj>SZeiD{3TYmki&(|i_QSz~nvC7S?qjZL#triE)rGIz6vdr9j#`57n}
zg=SdEYH5k`39Cs;a%PBW8Y)Rh8k@5`@M9I$vCni;(bJJ;|96&`U0By9#jbl5kFUQ#
zxJGuEZ;}9`KL?+rzGZ}&TY-lbo9{m%Mj;kHSu-_RZAlRhHs%ZePBC$cDVR8$s7ivz
zt5O)!n5VF;1&yhISHUZrfX)wKV+W;o&<vBYsInMn*R~y_u^DIxMVt?O$^dw+yt*B$
zxgDdrvZ*m>xjE>p0VczcnQ=4KZLFnKLqs&Cr6kyxKOW@}71cFUN)l7(Q3^ISk~g!E
zQ4r56QBx0-5I1L$U>D{Q<TT(>xw}kUl%0!-Pgf<8Wo<yWpN74XkgTYVrIeR}7AI?p
zQInjV5f?wBlH6oraSlc$c@}0V9`1?E0<65;f?VeP?kW0~BHAvFK8g|Gx>@r7f0pUs
zv${Yhnxc;H8M8u0{ggpN*`P%$j1!>adXPcB6HMaZk-rmAQAQmxzF++Ozxc$MTLgac
ziT<f!Zh;K=u`o#f{|`Rnh#g*gE1N2V_Va;ianQkVpotx8MnPlHY=kg7Xv3wsvZ*3y
zP&0*5=kE%r6;O*AC#EEW_jI3N)B)||X8iJ}24Q!~i4%W?ex*$N1v<``0bw^k18ASJ
zDr8gz#ZI_=f>2kniyE6^_%;G&C1^-c=XeUTf2V=%18uuz{E~74(slraEoe-ipFxxX
zwBH+i{ur{ojG!Iv#=^>`%7Tj6Z3G{9d4dtmvVS~`6H<N$q%cl^SOxJTc>V}vmm~w0
zxMYOe!VI<*!*_c?yW5YaFs6WLL}32ofmri<3*!WEc(Q`x65J*NoiPe(i(#=1v~OKl
z(NqzJ(QRY=asslAKILBl<AfAYD-$_1Pk^@NgBxro7>Kh^(NvKchLuf?(d}ba09gUG
z><Xh!%83)W>;tVEK%B$L0BK-CTCAYKK+p&>Xy5>}Ob~QZDq|eughhLHLv~y+r9(DW
zfOcV6v-m(o!TT#tSVK2mfaD<MJ*a`{1K#T-4_*m_wwDLpCRWG_IB<ItWb4wMJD2_|
zSiE}=Qwox;|MFqS3i*JfL5dhBEZ&V`eG|&TLZC4(kb}TyrtmWm=N9n6KA_q|%?#w0
z#k--Fp@hIc@X19yP}^91_ACPL1zC#Wpc4`T5Lc7s7H9(-TtdZx_OgK7vUvASRJZ&F
zouu@yK$IKg9u{!mEZ)6m5lB5s;G74Y$t3U>+>ZmN1@L|}dGHuIQVfB{jbLd2R6~RK
z?9kQ!;F%$i0QhusCN+>9AUA=!IgE!uRr5bPQCZpjj4lEK|Mm!oL8juwAdXtP6FkOc
ztqDpfVCKtzPx#dM{ypL20xhCG0h;duT?xc+lYxo(I?FupntE|Db~Yw<RU<KWB@RY*
zHf=_AHSm6QMJ3QW5C$`IGh=2+P7Y?~8BB~i5`uh;|GxkG%)-OR$;rve6risD?~*tN
zmw-1T?`~F30hW23T!P&Hg4r3Bh5nuTca)W%gIkpIX<nYNs7M&2{$)1M+Bb06u>523
zVStAXWSAN>d;&g67(8AL3L9{~!5TK8v;zv2e|Dfy0iCl3I^l8Y&YesjF#-o17LZ&7
zW<vHYVGSIJ+aUcm&=Ky?c++Qu_rF2CGw?tU*rjH+D4VQ|6`9_Fj_wuZ%l&tPUz5L=
zpP$hpmyhG$`#E4|enIT9TFR&cnoa#zAj$z&%-_qe305rn?+V1N)|yjJ{JRFphf8;Y
z)+h5Z@UbjnSp@Azp{_SW?m@9Ydr*uI-`$)W8$0(Vgkn7W>`-ChArSGedO=aqf+Chh
za7mCFcZlSnXNQW5K=L3vA!Q9{4Iud35m2oQKKc)~RuOV?9(b`Ps1FUQg~9htd==s1
z``5^)&ik*Gms9kY?7vpfwuRqYWSP@bz#d;D#{VyjpPz9RzZmGYi9a>6qM%9T6eA;~
zyu5}%6ms4PXw4;f8Wl8+0nR4ojCPEmG8Qu52eOyRow16KOZ1D9*a;DdUn1OmjP-o#
ze}5v`JMW(_zrrsOIa$$Piu{c01(^QSBWwkY6M)KZZw3(tIR+(AI~Q^m5!9=ob#O-F
zkP&UTS9=%>*+tpA+1dYo25pbJ16nCB%6<i5C(~udLWtns&m0_#${Zq~<@SFsA?$_B
zJNmH9X5eH1op7bjz#t4h1&Li<%>>-%2Tg{sg66H&%~5^a-p8lK=X=GMk4fwjpC}Wf
ztmxm>q8xk|W?lJbi*N$-|2{rG-zz@+TC*<kaftq12^zTLyD<C8Un_(wV0*(M?GC6r
z7(t``!Xj+?jO?IOLd}HXOQ=!Z!5GIB4l{K2B|eVBKKxpIAWQ%KLO6qE1F{2WUE&k{
z8{o^w2lm3h2!u<R7+C(lVcEd4o`IEtgMmSnlTlESlTnnBWy3$&f5-k6vaI=Q%(Ob4
zQRH7C<8o%1KR=%_?*vstpu7vZCkB*OB^kiGm$VrL6+vxKP&r}^VjF|b`T>o*Py6cv
z-f;2@)apt}VSe#vov1A6<Vaaj<`*JzphXs-#uz9~xU$@3*}x#iAO+rU$qw1`fyG!x
zaqy)G&`}&v(qS|+V)0@6cQu|<l5^_66UY`aZU8ORjzL<e&8W#P%KTy~NHynV$o7Aj
zJN}*nFVv1kEYxPy1f5gNz`)DO4t1Ly$j9vJdW`I#Rh*zH4A4BEprRny%OJO*3_d|!
z2^~ZNuij<xNpIs<75Mk<-*yz2eskmJZ*J!2cjMOt(VF~deC8MbKBu?wGxPoXhUV-Z
zTw0(;K*|?Ua6bZcx)bQ+3_<YGZdigBw6a1LRQw<s!lou>EIy#SZ2s{eyLcLSV-a}q
z2Bg_9%lrZ~WjYPbc`IZ^e~I!v<KuhAC;AJrrwA0^45;o=2kn}{<{oe#2fmR4x~74}
zXDMhW+s}V_$S!&f?(TpqA83Ds`NhA2rTqN=zN5LR^(nvTFA+I8kzb<xPeJE|GBAMl
ziTN;q*7t}*MnNGZ9%8+gFzCn`&?yYR17ukyLyqA{0bRWUSxWWq%3tUL_kUMFX_}3J
znW2_tEz5KUP`^eNd}c1_MjU7>(H3-!l&OgsXpS4w6ak$l1v<G=mQhg@w$g%C+0>c|
zG#?0_tX=+tkM9R)Sq$R@(ZBkTg()XeQvNl4=Mx2GDp9`g{LGChDJjOF<#WbJtEE6)
zz<;gakU4SU?`d#2fy6+|aw<=N#UXhcG!_i1m&HIUIbmnVfhq%2K|K~w{#G>-lLxJI
zV&Vsd43dl;=m<P{MnPji8%8@OW3Vz~K@&S><~T+XNmcMNfADGv)qfAgB$*gNizc`b
zqKxcZ!n`7qOw1{)oTB{Vl8lVtV_;YeON2z^e}flRF!nNXNXwhbF{aDAOqAr|{bwg*
zZ>Y)5{m&89L}A>|!mp?2Aj5cwiCvJF8zjGoF%E+bzAFPX#^S?Z47&9Ow0}xaL=19}
zjhU&SBItZyP)vgkwJ}lCXEaq51+|zI1x?hHO<6X8y#PMX##{0qkEA#BTpJ@vNyZ7@
zl8j#@y;*!L7+JYQ`2KW&j*+pj;8kVp`d6mPYXK2vu7C;uD`V_Z<+Xs1-GcfWJPa}n
z3JfX?8Vn{3;J%G4qcW?hx}rIFasX+#33AjjtEnohsG=z=cq=u#qPn8FG1Fqka#7jL
ze>YG3D@b81|ECHXT({QzcLm%u(qY;I>e2m?0Cmh|MgMiCfL1$9IFSije>E*B=>%x9
z0MT7ZIdS6O_twR&$4;C``yncul5&E9kwJh#fMq_*e9*mt!sce6AvrdF(8?8a&|P7m
zD|6VC^w`w(Sj`0&3F`56^RWq*r~T<I7c>?=Aj~efn3-44(8oVDt-{)?+|5Xso00kb
zB0)CcLn4NPWqp5A%LG~YI{5Sj7csJMh$@&mCtG{XtSJt4K(uK=>o1g{r!un0gQgNd
zt7L2$)fCu8ML-MY)odB<ScSl6SBXLwi-MZDploK&2s#yk@z0KBr78A0oSd>Er$l6X
z{~hWDGnjb|t<y?Q6$Gh?F)}jd@`*xnxhNlFE|ZuVi;t+HhK3Rs*9lPxW+i4N(Mb{#
zlSGx6m6#<&Pq6T+t7t3ydm=0&!^LIv?;^jE2>8&AD<VexjQU0_yo!>Lau}5MKxe&y
zR+2I>vMZ_!f);s#F{ri&Wpr^x(5^5OGejeZ#plG|6(_*p&pOEIIy{|!ygETAeSlBi
z`5hq3wCBVLP`@2?03B$-QVL{g8~A=4a9a<Ow;^SmECU1RBr{P#P`XB9fo5qyhxv*!
z?MVU6%UCm}L+HQKpaye_CU}7)WX1_302;m2wEpve5q!D<s0?9+pLqfre~<^C7tR2h
z0Ygc=f{KEy%A&@Cil)Y((G^xfcF?&|g2sX;nD&5{WdC)k*|0%^x#b@ZY)}V0n39sh
z;<KUV9}h?g<CmHZCqVXtMk-QL7<E90{4s)#`UCAIW+2)fpph3*Wk@XGaR+2gEN)kT
z;s+G`$gW^P2@7dZPY`=Z2!hU2RAdFYLQzl=YZ!n|Mg)ZgqYkzp_;)1*?DZ7zTpX&s
zV&D@3pnWV*MgW!L;)05TjJk}xjF=$~YLzig_;&@1J^y&7O`A3iOZbX2D1cUmV)Grd
zqM#zXvJxA3w=(QpG-WhDg4Z;rG^tsdX*T`iY0@;aRKsHZ-xZ8sQo<Y^LqMw{LmVB$
z7#N6;XXNw-8QEi2HZ?|b2TFQN0iEcG#Tl>!2Oj79$CH9|&JQTfgU0tjcl%&Z_2Ba{
zA@+lMQ;e8y0G)byC50&>CFR856`*vDHPQboVEh7hzzI;QNI?q+83rYA`HU^r*_BPr
zL5Ffe9Dy|uj;9<4ouxS;CFR6%ta<KV!3m=i(^9|=0EGakY~y6$WLeI#9Nce4Umb(o
zZ)OLzZxq!{%@r9Re7HR?E^gj!2*r5x38?1`B62{95Of?MqYle*xB`$mpTa{R1&5v-
zx^MzC{0b^_SQwD)2HmQJyVuSx$}TRdtPURB1`q1uc7st0=yWB}k)F8SkYbc#1e%>r
zNl9T~VEX^*|6K50bPC|JS&SgJ(Sgczq~nx9H`0NwrL$uMHMYUsd_-d#bn5(f_-%C!
z;7xI|_W}yS9GQ|<RZYV<bRK+--N&_%A*y13*K9svp_Elge>X$cw|o&0_yRw29_+q3
zEQeVf8Dzk%AE^7pMcCLGLG5qQl6Vs{sN28|Z}|30(5drgpgpnQ^0w({>s78|tcd#8
z#;eNvubI=o5aha*&rnw`9hVT61l_X7zqTTIi40?gfWW`W5C;mUtW5qp=?U_|^JgT4
zq4PMPv4LC$2?hpLQ$<E&Mq^G8!Ket?{tenSr_8j6al*e(t3c?=zbjz+6XOIUP{&eM
zG$n;mhf#;o_uuA!SN>f|k%d)%42%qw3=AyhEVIG=OC!)JlAsAM&^)8ElDa6UkqsJd
zFg68UXKHE!-m7C~Y$R?1iY}&zxXDtH)z(IwnO^3)kQ4WG&Al>N^er>JScRAtv&=4x
zWNVJHO_!II^2(I^4YBvPT&9<hh-ieVS0)pa0Bgx&a9dBEL4>7~We$TNgBF7mgBL>*
z=#FtlF(D>)(86AIH6|f*J4PchRwc+HBYDscU~@Bb&;ghFjB4s?;wTc}9dclCG3a5j
z=3sd~(8aCdpt&y4jFy@{BS;@;3Jg<%`4i|sso<M;ZU(~`|7=-UDmN;z@baiIvxu`v
zySpWD^D=WXbF<5^um+S$u<-CIv9O4-2?bj6@PNgCCif&K_aujb#H_d>VjK{)ygW)Q
zy(oroAWUcel>hJQzpMGd;o-sgjM|LaAeIcH(#A?2F=kFiu>?1FX=YI#WnMOxiA)lu
z0lXklQL8{9W>Ics9(I;rAX}3~MXdq_Ss)^CRVv7u;U<FW4Dgwrpt)Qg27U%H22g3q
z0P4`2Dni$(Fe;legU+!uHD(6yX8^UZwHcZAFzRgix8`39KNpidqt1zxMOUsYO4;(S
z6nrfO*I{Obe=R3YFn#9Y|99oYp9fd2oB-_{W$Zq27;<+EE8JdA1|9}s1}V_KZ$?Ey
z@Zq$^ij2yp!pfq`rpo3ZOKll7L2X-YMpKZr{}%nd1+~>~QOcq#tNzX9;%BU3;^60+
z&8+bE2V>mdTP*Gni)&7txbiQBpUd2wi$4HVK0^B28<@W^a4>*sCkD`YM&gQ~Lpni6
zyoxG{DuT8k*fB90Gb=JGvUDv<N%?nW(UreKC#0nm`9KqVCsOznrI?C8eE9Q#QHSvh
zv%-lKMx85HSOm3IQvNMsJfxy6$nviMR4IYmnvn5K9|lPVBk(EY#-RKKCLmKaptd-8
z^qtidvK(JoQAr)Nam&nD6f)$?2sr_dsVhYkvIhgSrx>)`iLn^elli-XQTW@xUDlcr
zc8of95r4N_IdK9s;sn_Z1@6%riOT+(W~~XjK8EERqZ^}(ru8(th={+>t}IGf!~j0m
zm1Ph3{3J8P8g$%lR2BqZo&Zh`Cg{#&JcP}eCr<DwN=ZYGz=yb#aSl#*{^R*~1r&;)
zt8P$S%D}+<e-Q%%s~F1_2GBV+@}MzO$cAjtj(E@kA1GU(MU73FCNb5DE9m@Y(NPd*
zsugo^LECos-~`Ls-y77-&DB@})c^UQ?sW#$WCZTJ0}T+#GJ@(g(0&^eGf+i_%!llX
zLdc`5N0&$EldOIv<b-<APGmTXaTc8U=M|iZB#Oi&r~q_}0Bo-~=(G?R8@#g-OkfuU
zOMwYO^6-4KfmMV-l0g$(LxWCDgv{l!D}u^!Gh;>Y!H~wd`-<QR_c%L487EDRKYuhd
zon#E{7~@h>PW-b&^c=f*MP+4SJx7)eCsO>)TY`;^gImn~Q%<D(sln_(LdMTQZewMT
zWY7l9w}E!jD4Vb=iiwJVwr=n<f|lJIE2^2AfO@W=*cWA0R5b-}zygUtM%0)L0xS>N
zFj@&QGkR#UE3N+<wMmIflhK2DJ;Zs_7={0MF$#kQ_FxWV6|vUT>{V6c5L6VKI8jVd
zkVEa?R9P)qP={DnOZM*yNOcQ8<903sJA(*=3S=!2<aBCK3KbO*S2P2i4~r-8c&BA$
zP0QM@r=+d^Z;!gR^1rhXSMheCb+=jOW`UIcdu?gulb!8jW%;iNBhV0G#?K(bpaW?W
zf_4Y9ffm}Ci<_&1a}F`?iqMZX&YzR7G$DVnl00G8v5I7OoB8GE``HEd=P#D}$A!n0
zptd`xyxzbn#300=#GnJ(Qz5D-BF8MQ2->l1#{`}a0mUcg$OKaas4u2!Z|^Jo_nnQc
zQ*OW4zeTu$Y{SBC%d|jy56R%YAt4LPyjlL#VT2jz>@^1H-T}}Cb5O+rx{(#sI5UT&
zEOF5N15ga9PCz|cCjB65J+prWW_l@%6U<_y)__vSiPGSI1;M4IpcLdM6=P;(WELak
zr}@v_Ob^_l4=ybYW}FaQ3JQZ1@R{+TvpYd|vSRe!ASoDRpExK<=`$*_gN{A7hi!Oe
zWmi;3UMtH2I-Z`N>)+D{yi%&X5r4mg^QuYlKKOT^3p5><!swC$s;~ZmhH$_qWc_sk
zAFA+xS5=xfB7#?1mG=Qy0Nk^H%^QKnzCg1^y5PH0!1IC1CZL-=*hJy9DP-gbJhp>$
z1`%jX88lvN%XorWA-FWfTJx8tHIytB1ns8#9bo+tTyug315-q0S$s-^!E0?n74C`P
zQqcNbP`^ym`X3Ku^cg}jFoRaEv8-fS!@$cR2--6UTD!*2s0dnv$!KQ8$qWfQP!uvg
zU|J<F-~3Ph?{5VKMs@kfe`^@S81?_HUc{^*D*LBKmZkS^sC=Y+GZUM9q&%aB{J*?^
zs~8O!L;pxaGdXx&R6hd)iy{kXtubge1HOL{bfJJK=srF0nP|))3&E`n(8?vqO=_Tf
zT38hKa;S^`D-c!Z*vla-%2X(;C0i>hyyEX#Q2RpE;DkQ7$-&gl_)AzuM))6_uned)
z5}hXVx5o%91xoW^wTO5Djp+$6$b)7y8A12XDJu#xg320Ds}3pvnJxmi^O##0bwEdK
zfGP#Xaz>pKe^-D6AP4n>1(+2;chNEVGwOtZTXp|<Kzx2KP^$WO<pcv0XkR0%2#Y&|
z8$%d)%@lZHr<j?!8F+1h7-;Vkqc$VxC=EVFP{i9af=)10XNU3BAj77h2`n?v-a>0e
zGtlM=(Ec3Ieh%>QDNhc2_hotV2notNb8$%ubFuP^^Jp?LhuPY(Tga$0D~YCvN-*;n
zY8{r)<c9L3LUiT}%gBn>aO>s^u#289VRVI8zAWyJ*<GG|Y+Q;8N~+QV9BdpsI=1%V
z<~3@sKvNcOd>jrSdahThDZ84ek*I9Ae>|UXYCJe2fC<o8%l~JrA}n<boD3=qCJZ(V
zK@3Td+rH%)#n|{6Rl#!);H)9S#?HzHS|+RrX{LxnPKN>c4s><B8te)K&><tBZ5)jD
zpq0Y>jNn~KW~%ILpv`iiiy!pBON1L4mkaChDey51bpO%La`uo*V>TC+l@b0cFRsL?
zW%cihT|~sr0AV4mHUVx0ZXw172VGqU-GBTugcRh21!Q!k><xGojRW*+G@CRy88y@7
z6;*j<1#N{{nMByR1(;jDb8ri&@oVu`oKP~mA}eYns>a^JBEs(+VRynVVvUTbg|UDD
zw~L_6NnMb^|86%4a|sFwD=RRwZnpK*cGck2pRQjk6Ctgp&CkhgDxfIh!N~)zf*@^O
zOO_1`feZ=YvueQ2T+q5`J|=!fBRN)45k6MXnWmt0V#|ocf&`p3c)L8j(GD6R2Az6p
zV#X}0Xk^D^YHp&(tjvhSW-6S<#qXcN$f%&FCm_fsWMW{YB`dCErt6?A1#S(cfJyMt
zF_1&r)`JC7_^&gv%V-L7GlF-^{`<+8jo^WfHp^Aa_0sdzm1kpP<`>a0^3%1Bwb2sg
zW`Z0Z0U^OAfsU}52D{|d8=Qr}Q%>LsoPPzNb2_=Xz#LH7&%(gOvVmnCgE)gGsQ!bE
zV}g#g0F9Wc>oI|<7#1Zy7Jf!?MRPq?W@X_k%sgRLDndfCjX{5p1U1QUNN7|sDfHBa
zxogX^vCUyJn8U=Opy?T^IQKBix~i~$3C7!<oWje?B4nbD8Z#~ktC}gUrmL&U!}Iww
zE5DA0q4K{&jI7|ZM;Vw~z<cLF^Xs4jOAu7+KuQltmk<;yjJKzyuL;roXQvsmhS9@J
zbegCctH_%4e>|EYA)1U|(%1ZvcqnRSCi)Pxp9*w01d^E|V#YYkOxQdP-OOp5|2bhZ
zlYv2mm5)V=L4-jH)FV-47Zn#(XE!xBHBqx;vIm`T$*O9~s>;gux9MM-U&^#;DU6Ja
ztStJInVJ}5{w?~qh%t^aj_Lour>krpOglbh>WOK;Sa{jE*qAjO8KwUwEn7Bu=~7VK
zq%eMA*}$y8z|H_VRhvP`2vq$-H!i?7Fes^+?5T-}sEJ^FflM(gM1Z+SB)C5eI)aD^
z)c1p$$pAGIW;Tq5nuer3g2e~S`*#AF0=L~$Kxeu_*9+>y=8z#V2g*61GzID-fzlH;
zel4hE=jZwZxyoi6s4IiPM(KqVfYUjc0G;v#>ydz(av)E#faY;Q>jlBB8BmiKc?Je!
z^&<L6G3X}OIL0sFwUa!c6<?<z{aElJ1b;Uo?Z$LxoWQbS_u_vbRZAJaEZvE6?Hg!s
zGI%Bov{?qa9u{<_3uv#94{Uc7sBH<_qKip`LJL$wL3$C^jHvW9(7G2$y8xAhoCym$
z*#t$L#Rt@DK*C5&&|wdt@i|5v23F=5EQ=Y~8F(3l!Q&-b3=E8NjBM<BjAo$g>6O$}
zMc9;0P}t1SGbhB1jhMQ^?d`+E?d|{ltl3brfoac%nhk#sz?q=EQyR>IEQ_5tZE|wj
zwCUf04K*8TK+^xNz*(2Ux7zoyfX?RK%5Z{Z3Ueca6ZmFFaYi{tc2E^=Br3wkqRprz
zXkxCW#4aW(pv|bH$82h1tgHmx<N~U__!#v;lZ@<adW>dFFI^=?7`3=L*jT0bIT%@Z
zrPb{<C+*-82@&Pq(P!YPF3HZs&M(Qv#=*^~B_ib`BFd#KV!VyHdYg`jGM5;0o-mux
zznk3h{CvW^vSw};Mly^F65=c@65{_JXxRiiEAtBT@yl~DY6!6lvnp$dvGE){$iph8
zp$tlnpmjr#xldIFXVB~;?mJ`{l}+@R!S~CE8;dHN8i8+@VHQ<3Rb<&<g!fLFzb=v-
zToM9;DU!TACyY`A1thpQBpJWJuXe$8!^~eHeqk<F#(&}5yo^Rhl8h@ES-FJyr-9rJ
z89Vb~S;rvD0NVA>z-X$ZuE(g)$f(Q++B3)xI{5>%YD^Kd&Q+9Anh{h)gOV*MlY_Q3
zGry3O;}c_L`FG#--xVQIPI+!_MnOYsAy-CKTV6%=e{b3<88<?X?g1@}5|w4tF;d`D
z;^Jm{{g+Q!lv9|M)yPO&Z^JSrM<d2|#;|{@{yv6nXqE-<@rQ&fKZ84H{{yI;LCNXL
zO6;I*r|fK?mK|tKEU0Cttfa1H&!`AmmkMbViNR#lmDJVLl^NB9m4%H!%UvLI;Id3^
zZoI7jt_lhXsqt`gG75rj9yAx=;<9Jtvg8)zkpS@-Ejf6&IRqso`5FH`VC6fS4qAc;
zzODv*O-T^XUw#E)ApuriBamt%4jyqn$;C6}B?NgmAR_EMjFQqSOmBGpu`+TXx2+-f
zXQ+eLM3`bMg@Enu0|h#$ng-2nf{t8a+3>pwv`!SXW)-w~F&ByB4w^?zIUy?w-q$D!
z+2i=H0KAf9f~YL`4mtRE0_2WztaEzk<toT-(5iYPB)gc<EI#o&08}_5+x?FR>PXO%
zY>4fDSAf^^gYr!Z_-+dDycQ&OL1!m{l9nv^nmt$@ql{dq3n~hN-H;*+F%nb`7l_J2
zDOslZj5?rh-oHhVu1yMr0xk1`+=2s|K4H{Bj{|WAbp{4wWkF-mkvqt#4CHV0umDY;
zAq9gwqYh~EC@6@*fzGl4<`{?zL0wDKZ~)~$(B3xCoDk^#EztRx(DslvsI`fFbsTtP
z4AMrjW;8Ws7gQ7k9S$WcD>@CbZW(N+C}SMNqo7?B;QlvKm_VEX4KMIbfhct;C=G)B
zuc$0&4BD$p$p0)GQd0hwp@zVpb&#Y6b`3Eh06H7pfH|L~fq@^?$^y?$37UXrrWnOU
zm=pgAi3{-w2=EDsGrr?vGW{ncE-lIYLQ-1%pAerGDEt^19T|+6TUhEq=ihUJj#(5G
zVFMkj&*;ziPF71x_MZ?RQ#Io|SwYP|5}JatjPIcO7>pPlS?ZWuK>9$b4!S-Ud|;L!
zNS7AhKOtE`<`%FKd?3TX_AwYS>af%?=Y#Zt))RwrC1{))-4c-7LH0Ag;{)kq0<A)2
z*}$T~z{bD@TA9UYs>^5!S|!J{=dTar+<(a|EB-uSR$zL%?cd(J2M^x;eFL<O2Yi+i
zs4W4yyBxHZ95gPZC<wlFlF``2jumu10UM-{)?)^pQ6R?(T6e;-0W@t6+Gg@Q09L1@
zfLC#W`vfVld&>Si09`K$x^?m&5A>R{zqdduxKhCD)A*Rw;P-<ufZf8t;=}xcK^1h;
z5U3u*bx#^-21XR>PG$vU7eH5(!Ck<t0GfdYEh+@9*+Oyy6C<V@KsRTC91A)x>jXm@
za}kRd=-e~Vcse^MGPD_$Kyh!*XvVa6fdCiJKLf@KJX`_`c)9**va>T@=VI{^<N0Us
z&wxjalkqy}I*aR^3=E8nb_{9EFW_cDuN(k%7R@;s)yx=!7w~d1UT0_jr^&?&GM4ee
zKLZ{v7B9|!nxK0xG&#k17%wnh;1L74D~;g<ix+bd$XyJeJqXZ*1*$H^IT=|@#L#px
zmm@n6Y(B`@;Q8V-MmrWS<`*EdI2l2Pv8i)1g3h5c0VlaE#_OCY{$YNI>O_#w8LxxR
zV`gHw`Tr-2Ez1T5Rt75u21alT4|G_JJR|6&Q8^Yi@D5@nHDx6>C3QV!(BO$3iyWgk
z=v1Z`8)`QE3ri7IWA|s}R?-MCcGuG8XBA{*5faxj_SNCF^5o`oHy5ysH&<h4=V#+r
z)v_~pGnAjq!^;9{@@+UFBYcRFiH)6|m0#Y}JJ*?A(%U`8%Sn)xm7QHOyCg?N**?S5
zTtS$fnT_>;kOU;ZoL~@TZh@{#hL2WiGYWz)_XbVngYHcbRW$ttx%g??e_<(5*|!?L
zB|`Qe&n8X*QKl|vkq4gm0gb;(g7>QNfkG9O^F;+&1yw<Z)|!|xikh4LVtS`;``3jj
z;;&1JJR@%k%Z5OmKhdSk+iL$vurP|7{EbZ!Ndb)rGB7gv<{xH>kLS1f${^3O0p#op
zWyRBB^#8#Bb6D@NiZC!Ra6s}7I8GT@MgF&f?i>6c`0onq9k59tEik;8aR=x;N(Kf{
zFAiiX%LdSCQVa}Gb^%itNH+rmjAq=(zyLZejo}6Z0~jx2U|<9djCeq3kQ~&EYL*S4
zmB0)qK<lVjHvIntHIsp5!++2nkRUtwST-<lLe&4i%(4N5;dX=MVOWi214ABYj1mc_
zuxwzEg6L)7fzskE8~)Ehuo=Xmc7kXa2I&Kt3!_17To|P01k?<W8dIp*Cs;Q8-^#Lq
z0W@w35=X;eHz14MhPt~H$t;jO7c`7O?72`j$UYDTh0{Nl4WJ|jj*||S4UE=Mb6|c!
zra@*P^FeHo*mjl;3?K|*gRm#m3=kWHV_7!*zXQedq5cDj!*B}AhX4N|?*1RhvVnmO
z!Un|?qbth>21W=U>}M~~S*{R1NFId2;)yI9{{IE#FElIxR(k}b4m6(+N|yg`Fr+Y8
zf_8{Asxzc8NTNWH0tmhdroh!5m<4TJLD}H34zP)!wXO^dDgWo7LeMIw|DXPUVn|`A
zWB}O%ZXhzG{9gn&0mL`~HiMC&pMe3SpCN^Th2aFqZ44=l6TosQj5?qjJwb}$IE8_M
zQ3upkU@!vJ#0(4!CqSh>LmI<PsBSF=2;9W*g5d?DA0D`biH%8#sg9|QX${j8W;f<M
z<{suFEF3IGEJ-YjSnjaKu<l|jV*A3bz@Ef@ibIYgiDMt98D|UUHqLuo8eBeHQ@9>+
z8*q1VpW@-*iQv`ZjpN<G$H$k!w}oGWe;NNX0U?19fe8Yu1hoXq1h)xs30Vl$2ptfX
z5-t(GAfhF5Mbt)gh3FYE7BMri1hEZb|HOmDmq{>5=t*QrOq19k@j&9Aq=saM<P^yx
zlD8z^NtsDyNbQjNCLJfeMaD>Gip(Qf8Ce(E9N8(ddt{%<>B*JJ9gt6wzog)%uusuQ
zu}pD?l7rGTr5(zA%1X)>%2mpTlpm-tsTiolsZ3LOq-vs?pt?s*M6FKknR<Zw9`zp@
zCK?4AYcv;Vp3zd%^3rP2=F+ax;n2y@*`@PFS4Ou(_lur}UYgz_y&L*``fmDl`kVBB
z7}y!K7+f+GGE6XBXZXTM#VF2bmeD<9C1X3|4C8Gk5+(^ID@-n#>X^<mePL#2R%3R@
zT*}<fe3JPU3jqr!i#CfBmRy!zmK~O7tYoZmtY%r=vtD3*$40^?z-E!n4_hPKH+EKb
zJ$6^@HSF{3SK0q^@Nj5wIOfRYSmz|+w8H6~vxKvgbD8r3=Tj~ME(tE{T)w$Rxo&e~
zb1QM%;`Ytm%)QKgoBKBpF%KgTKaVVrHjgu&LY@|$ah@%ntGt-J^t^&Vr8c8D!*bA>
zGT<2-P|eTCz`)=Do|Oe9I{}7cAXN+u48owA1~iMr0y-fwi$Ms=W@OM{sDZMX82A{L
zK-tU;J`9_nY!(I!hJR2tD?<gN8I;Y&V8*x(%I07YV|)T-b25lA{(`c(8B~}Apllun
z6($oXTbMzGDFVtq!63#2bHhmn1Ext3@te#HQcQ=+%ggoBb1DlmGL!T3iuIE7a~Yf&
z@)-&kDjA9xG8xhtG8jr26c|=AC@>f?7%&(zm@p_XBr#MnC@^?2Br@bNC@^?4<TI2p
zB!bmvGw3lWFgP;gFyt^OfJGoC6oYxG48;to3`GoO45<t$40;Ui3_c8z3;_%t44w?m
z4E_v$48aTv3?2-L3^@#G3^@#$40#Oc48;sV45<w145bV?U|S3s^cV~n3>YjKtQkTX
zycxn7{1~jUnuTeSE}B`yx<CO<Ul`aQ#SEDY`QR`>c2fY@7a$iRi-JzrFJVXqt1n|v
zV1UT!F_<$LFjzC>G9)r&Go&))Gn6o-G3bH)p2T3ppvPbe#ug05;B`pY+)LP(*kcO2
zXgLE2>M^7<<S<k+6fk5kWI|nC%%BHm<$_g|Gf=K3oFSDVi6IRfCZJRX2@{ZL3WEZJ
zKSMG&?Sa-`fyS-O7_1nK89<}~gE>-qbc5>;W+-GRWyoYGVklxrWypc%S_TG&|63VA
zyG22H76mhkGcYhRF)}kSGw?F-F|sgBV`ODyV`OLKVB}=vV&rDzVdMqRKMF7kG72#Y
zGrVUMVH9N)V-#nUV3cH(Vw7f-VU%T*W0Yr9U{qvOVpL{SVN_*QV^n9<VAN#PV$^2T
zVbo;=4YnCD8ZsI&8Z(+OnlhR(nloB3S~6NOS~J=(+A@Of{dQn<WOQP5W^`e6Wprb7
zXY^q7Wb|V6X7pk7W%Oh8XAEErWDH^qW(;8rWej5sXN+KsWQ<~rW{hEsWsGBtXG~yB
zWK3dAW=vsBWlUpCXUt&CWXxjBX3SyCWz1vDXDnbWWGrGVW-MVWWh`SXXRKhXWUOMW
zW~^bXWvpYYXKY|>WNcz=W^7?>Wo%<?XY63?Wb9(>X6#|?W$a_@XPm$|k#Q2^WX36s
zQyHf*PG_9KIFoS}<7~z`jB^?1G0taPz_^fc5#wUUC5%fMmoYA9T*0`KaTVig#x;y<
z8P_qcXWYQJk#Q5_X2vayTN$@8ZfD%VxRY@g<8H=1jC&dPG45wPz<7}H5aVISBaBBG
zk1-x+Ji&O9@f71}#xsm(8P74EXS~38k?|7aWyULvR~fG{UT3_)c$4uK<88(}jCUFD
zG2Ul<!1$2y5#wXVCyY-SpD{jXe8KpV@fG81#y5;_8Q(F!XZ*nUk?|AbXT~p#Um3qK
zerNo__>=J$<8Q`4jDH#bG5%*_U}9uqVq#`uVPa)sV`69GVB%!rV&Z1vVd7=tW8!BL
zU=m~!VgnbIh7ih#y)-Y=z`)S}L>n0xz-U7#A1rTVU|<a8n?Pw(C~XF%&7rgfl(vM@
zju6_&4MLl`vAC8N<wH15E@0Z&&<RYNx;cYsGXsd_W^NG8<^~YM%niVXxEes@T@4Jt
zj&p@N&egyG>?l_Q1F)l94Gh4Jay2jjJId7nVvehUA=s0y28LjJTn!AN<`{xK>1tpI
z_N1$UA=ErWsCkA^^9-Tp8Crte>uO*GHO~lYo)OeMBdB>sQ1gtS<{3fFGlH6D1U1hH
zYMwFFJY%SN#!&N&q2?Jw%`=9YXACvZ7;2s|)I4LTc_vWvOrYkOK+Q9Onr8wv&jf0o
z3Di6jsCgz(^Gu-TnL^Dog_>syHO~}ko+;EkQ>b~SQ1eWo=9xmxGliOG1~tzNYK|Gy
z95bjnW>9m?pyrrC%`t<TV+J+H9BPg^)LrIK^UR^<nM2Jphni;&HP0Mso;lP!bEtV1
zQ1dLH=2<|^vw)gs0X5G8YMuquJPW9K7EtpnpypXZ&9j7>X9+dW5^A0$)I3Y5d6rP~
zEFtC@Lesq=G~F9QGNr4b0Ysl6H2oVw)4w4!{To8lzacdJ8$#2+AvFCPLesw?H2oVw
z)4w4!{To8lzacdJ8$#2+AvFCPLesw?H2oVw)4w4!{To8lzacdJ8$#2+AvFCPLesw?
zH2oVw)4w4!{To8lzacdJ8$#2+AvFCPLesw?H2oVw)4w4!{To8lzacdJ8$#2+AvFCP
zLesw?H2oVw)4w4!-5WyFy&*K+8$#2)AvE0^Lesq=G~F9Q)4d@y-5WyFy&*K+8$#2)
zAvE0^nnBWoAvFCPLesw?H2oVw)4w4!{To8lzacdJ8$#2+AvFCPLesw?H2oVw)4w4!
z{To8lzacdJ8$#2+AvFCPLesw?H2oVw)4w4!{To8lzacdJ8$#2+AvFCPLesw?H2oVw
z)4w4!{To8lzacdJ8$#2+AvFCPLesw?EZti|@{yq>Bp(?;Dk)bZXnrt)<_9BaelUXO
z2P0^HFoNa>BWU_Jf~J2XX!<vTrhg-7`Zt26e<Nu6H-e^rBWSuef~I>TXnHq-rgI}`
zIyZu*b0cUvH-e^fBWSudf~IRDXu39nrfVZ;x;BEQYa?j7HiD*WBWSudf~IRDXu39n
zrfVZ;x;BEQYa?j7HiD*WBWSudf|lz>(DZEtP2Wb)^lb!9-$u~%Z3Io<M$q(a1Wn&Y
u(DZEtP2Wb)^lb!9-$s!1?PlTT##U<RW@=~%qRbo}L9JKDkbuMD3=9Ave@L+a

literal 0
HcmV?d00001

diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.woff b/web/root/bootstrap/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000000000000000000000000000000000000..9e612858f802245ddcbf59788a0db942224bab35
GIT binary patch
literal 23424
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

literal 0
HcmV?d00001

diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/web/root/bootstrap/fonts/glyphicons-halflings-regular.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..64539b54c3751a6d9adb44c8e3a45ba5a73b77f0
GIT binary patch
literal 18028
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

literal 0
HcmV?d00001

diff --git a/web/root/bootstrap/js/bootstrap.js b/web/root/bootstrap/js/bootstrap.js
new file mode 100644
index 0000000000..5debfd7de9
--- /dev/null
+++ b/web/root/bootstrap/js/bootstrap.js
@@ -0,0 +1,2363 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+
+if (typeof jQuery === 'undefined') {
+  throw new Error('Bootstrap\'s JavaScript requires jQuery')
+}
+
++function ($) {
+  'use strict';
+  var version = $.fn.jquery.split(' ')[0].split('.')
+  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {
+    throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher')
+  }
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: transition.js v3.3.5
+ * http://getbootstrap.com/javascript/#transitions
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+  // ============================================================
+
+  function transitionEnd() {
+    var el = document.createElement('bootstrap')
+
+    var transEndEventNames = {
+      WebkitTransition : 'webkitTransitionEnd',
+      MozTransition    : 'transitionend',
+      OTransition      : 'oTransitionEnd otransitionend',
+      transition       : 'transitionend'
+    }
+
+    for (var name in transEndEventNames) {
+      if (el.style[name] !== undefined) {
+        return { end: transEndEventNames[name] }
+      }
+    }
+
+    return false // explicit for ie8 (  ._.)
+  }
+
+  // http://blog.alexmaccaw.com/css-transitions
+  $.fn.emulateTransitionEnd = function (duration) {
+    var called = false
+    var $el = this
+    $(this).one('bsTransitionEnd', function () { called = true })
+    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+    setTimeout(callback, duration)
+    return this
+  }
+
+  $(function () {
+    $.support.transition = transitionEnd()
+
+    if (!$.support.transition) return
+
+    $.event.special.bsTransitionEnd = {
+      bindType: $.support.transition.end,
+      delegateType: $.support.transition.end,
+      handle: function (e) {
+        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
+      }
+    }
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: alert.js v3.3.5
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // ALERT CLASS DEFINITION
+  // ======================
+
+  var dismiss = '[data-dismiss="alert"]'
+  var Alert   = function (el) {
+    $(el).on('click', dismiss, this.close)
+  }
+
+  Alert.VERSION = '3.3.5'
+
+  Alert.TRANSITION_DURATION = 150
+
+  Alert.prototype.close = function (e) {
+    var $this    = $(this)
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    var $parent = $(selector)
+
+    if (e) e.preventDefault()
+
+    if (!$parent.length) {
+      $parent = $this.closest('.alert')
+    }
+
+    $parent.trigger(e = $.Event('close.bs.alert'))
+
+    if (e.isDefaultPrevented()) return
+
+    $parent.removeClass('in')
+
+    function removeElement() {
+      // detach from parent, fire event then clean up data
+      $parent.detach().trigger('closed.bs.alert').remove()
+    }
+
+    $.support.transition && $parent.hasClass('fade') ?
+      $parent
+        .one('bsTransitionEnd', removeElement)
+        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
+      removeElement()
+  }
+
+
+  // ALERT PLUGIN DEFINITION
+  // =======================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.alert')
+
+      if (!data) $this.data('bs.alert', (data = new Alert(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  var old = $.fn.alert
+
+  $.fn.alert             = Plugin
+  $.fn.alert.Constructor = Alert
+
+
+  // ALERT NO CONFLICT
+  // =================
+
+  $.fn.alert.noConflict = function () {
+    $.fn.alert = old
+    return this
+  }
+
+
+  // ALERT DATA-API
+  // ==============
+
+  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: button.js v3.3.5
+ * http://getbootstrap.com/javascript/#buttons
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // BUTTON PUBLIC CLASS DEFINITION
+  // ==============================
+
+  var Button = function (element, options) {
+    this.$element  = $(element)
+    this.options   = $.extend({}, Button.DEFAULTS, options)
+    this.isLoading = false
+  }
+
+  Button.VERSION  = '3.3.5'
+
+  Button.DEFAULTS = {
+    loadingText: 'loading...'
+  }
+
+  Button.prototype.setState = function (state) {
+    var d    = 'disabled'
+    var $el  = this.$element
+    var val  = $el.is('input') ? 'val' : 'html'
+    var data = $el.data()
+
+    state += 'Text'
+
+    if (data.resetText == null) $el.data('resetText', $el[val]())
+
+    // push to event loop to allow forms to submit
+    setTimeout($.proxy(function () {
+      $el[val](data[state] == null ? this.options[state] : data[state])
+
+      if (state == 'loadingText') {
+        this.isLoading = true
+        $el.addClass(d).attr(d, d)
+      } else if (this.isLoading) {
+        this.isLoading = false
+        $el.removeClass(d).removeAttr(d)
+      }
+    }, this), 0)
+  }
+
+  Button.prototype.toggle = function () {
+    var changed = true
+    var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+    if ($parent.length) {
+      var $input = this.$element.find('input')
+      if ($input.prop('type') == 'radio') {
+        if ($input.prop('checked')) changed = false
+        $parent.find('.active').removeClass('active')
+        this.$element.addClass('active')
+      } else if ($input.prop('type') == 'checkbox') {
+        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
+        this.$element.toggleClass('active')
+      }
+      $input.prop('checked', this.$element.hasClass('active'))
+      if (changed) $input.trigger('change')
+    } else {
+      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
+      this.$element.toggleClass('active')
+    }
+  }
+
+
+  // BUTTON PLUGIN DEFINITION
+  // ========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.button')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+      if (option == 'toggle') data.toggle()
+      else if (option) data.setState(option)
+    })
+  }
+
+  var old = $.fn.button
+
+  $.fn.button             = Plugin
+  $.fn.button.Constructor = Button
+
+
+  // BUTTON NO CONFLICT
+  // ==================
+
+  $.fn.button.noConflict = function () {
+    $.fn.button = old
+    return this
+  }
+
+
+  // BUTTON DATA-API
+  // ===============
+
+  $(document)
+    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+      var $btn = $(e.target)
+      if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+      Plugin.call($btn, 'toggle')
+      if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault()
+    })
+    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
+    })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: carousel.js v3.3.5
+ * http://getbootstrap.com/javascript/#carousel
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // CAROUSEL CLASS DEFINITION
+  // =========================
+
+  var Carousel = function (element, options) {
+    this.$element    = $(element)
+    this.$indicators = this.$element.find('.carousel-indicators')
+    this.options     = options
+    this.paused      = null
+    this.sliding     = null
+    this.interval    = null
+    this.$active     = null
+    this.$items      = null
+
+    this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+
+    this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
+      .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
+      .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+  }
+
+  Carousel.VERSION  = '3.3.5'
+
+  Carousel.TRANSITION_DURATION = 600
+
+  Carousel.DEFAULTS = {
+    interval: 5000,
+    pause: 'hover',
+    wrap: true,
+    keyboard: true
+  }
+
+  Carousel.prototype.keydown = function (e) {
+    if (/input|textarea/i.test(e.target.tagName)) return
+    switch (e.which) {
+      case 37: this.prev(); break
+      case 39: this.next(); break
+      default: return
+    }
+
+    e.preventDefault()
+  }
+
+  Carousel.prototype.cycle = function (e) {
+    e || (this.paused = false)
+
+    this.interval && clearInterval(this.interval)
+
+    this.options.interval
+      && !this.paused
+      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+    return this
+  }
+
+  Carousel.prototype.getItemIndex = function (item) {
+    this.$items = item.parent().children('.item')
+    return this.$items.index(item || this.$active)
+  }
+
+  Carousel.prototype.getItemForDirection = function (direction, active) {
+    var activeIndex = this.getItemIndex(active)
+    var willWrap = (direction == 'prev' && activeIndex === 0)
+                || (direction == 'next' && activeIndex == (this.$items.length - 1))
+    if (willWrap && !this.options.wrap) return active
+    var delta = direction == 'prev' ? -1 : 1
+    var itemIndex = (activeIndex + delta) % this.$items.length
+    return this.$items.eq(itemIndex)
+  }
+
+  Carousel.prototype.to = function (pos) {
+    var that        = this
+    var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+
+    if (pos > (this.$items.length - 1) || pos < 0) return
+
+    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
+    if (activeIndex == pos) return this.pause().cycle()
+
+    return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+  }
+
+  Carousel.prototype.pause = function (e) {
+    e || (this.paused = true)
+
+    if (this.$element.find('.next, .prev').length && $.support.transition) {
+      this.$element.trigger($.support.transition.end)
+      this.cycle(true)
+    }
+
+    this.interval = clearInterval(this.interval)
+
+    return this
+  }
+
+  Carousel.prototype.next = function () {
+    if (this.sliding) return
+    return this.slide('next')
+  }
+
+  Carousel.prototype.prev = function () {
+    if (this.sliding) return
+    return this.slide('prev')
+  }
+
+  Carousel.prototype.slide = function (type, next) {
+    var $active   = this.$element.find('.item.active')
+    var $next     = next || this.getItemForDirection(type, $active)
+    var isCycling = this.interval
+    var direction = type == 'next' ? 'left' : 'right'
+    var that      = this
+
+    if ($next.hasClass('active')) return (this.sliding = false)
+
+    var relatedTarget = $next[0]
+    var slideEvent = $.Event('slide.bs.carousel', {
+      relatedTarget: relatedTarget,
+      direction: direction
+    })
+    this.$element.trigger(slideEvent)
+    if (slideEvent.isDefaultPrevented()) return
+
+    this.sliding = true
+
+    isCycling && this.pause()
+
+    if (this.$indicators.length) {
+      this.$indicators.find('.active').removeClass('active')
+      var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
+      $nextIndicator && $nextIndicator.addClass('active')
+    }
+
+    var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
+    if ($.support.transition && this.$element.hasClass('slide')) {
+      $next.addClass(type)
+      $next[0].offsetWidth // force reflow
+      $active.addClass(direction)
+      $next.addClass(direction)
+      $active
+        .one('bsTransitionEnd', function () {
+          $next.removeClass([type, direction].join(' ')).addClass('active')
+          $active.removeClass(['active', direction].join(' '))
+          that.sliding = false
+          setTimeout(function () {
+            that.$element.trigger(slidEvent)
+          }, 0)
+        })
+        .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
+    } else {
+      $active.removeClass('active')
+      $next.addClass('active')
+      this.sliding = false
+      this.$element.trigger(slidEvent)
+    }
+
+    isCycling && this.cycle()
+
+    return this
+  }
+
+
+  // CAROUSEL PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.carousel')
+      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+      var action  = typeof option == 'string' ? option : options.slide
+
+      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+      if (typeof option == 'number') data.to(option)
+      else if (action) data[action]()
+      else if (options.interval) data.pause().cycle()
+    })
+  }
+
+  var old = $.fn.carousel
+
+  $.fn.carousel             = Plugin
+  $.fn.carousel.Constructor = Carousel
+
+
+  // CAROUSEL NO CONFLICT
+  // ====================
+
+  $.fn.carousel.noConflict = function () {
+    $.fn.carousel = old
+    return this
+  }
+
+
+  // CAROUSEL DATA-API
+  // =================
+
+  var clickHandler = function (e) {
+    var href
+    var $this   = $(this)
+    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
+    if (!$target.hasClass('carousel')) return
+    var options = $.extend({}, $target.data(), $this.data())
+    var slideIndex = $this.attr('data-slide-to')
+    if (slideIndex) options.interval = false
+
+    Plugin.call($target, options)
+
+    if (slideIndex) {
+      $target.data('bs.carousel').to(slideIndex)
+    }
+
+    e.preventDefault()
+  }
+
+  $(document)
+    .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
+    .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
+
+  $(window).on('load', function () {
+    $('[data-ride="carousel"]').each(function () {
+      var $carousel = $(this)
+      Plugin.call($carousel, $carousel.data())
+    })
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.3.5
+ * http://getbootstrap.com/javascript/#collapse
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // COLLAPSE PUBLIC CLASS DEFINITION
+  // ================================
+
+  var Collapse = function (element, options) {
+    this.$element      = $(element)
+    this.options       = $.extend({}, Collapse.DEFAULTS, options)
+    this.$trigger      = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
+                           '[data-toggle="collapse"][data-target="#' + element.id + '"]')
+    this.transitioning = null
+
+    if (this.options.parent) {
+      this.$parent = this.getParent()
+    } else {
+      this.addAriaAndCollapsedClass(this.$element, this.$trigger)
+    }
+
+    if (this.options.toggle) this.toggle()
+  }
+
+  Collapse.VERSION  = '3.3.5'
+
+  Collapse.TRANSITION_DURATION = 350
+
+  Collapse.DEFAULTS = {
+    toggle: true
+  }
+
+  Collapse.prototype.dimension = function () {
+    var hasWidth = this.$element.hasClass('width')
+    return hasWidth ? 'width' : 'height'
+  }
+
+  Collapse.prototype.show = function () {
+    if (this.transitioning || this.$element.hasClass('in')) return
+
+    var activesData
+    var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
+
+    if (actives && actives.length) {
+      activesData = actives.data('bs.collapse')
+      if (activesData && activesData.transitioning) return
+    }
+
+    var startEvent = $.Event('show.bs.collapse')
+    this.$element.trigger(startEvent)
+    if (startEvent.isDefaultPrevented()) return
+
+    if (actives && actives.length) {
+      Plugin.call(actives, 'hide')
+      activesData || actives.data('bs.collapse', null)
+    }
+
+    var dimension = this.dimension()
+
+    this.$element
+      .removeClass('collapse')
+      .addClass('collapsing')[dimension](0)
+      .attr('aria-expanded', true)
+
+    this.$trigger
+      .removeClass('collapsed')
+      .attr('aria-expanded', true)
+
+    this.transitioning = 1
+
+    var complete = function () {
+      this.$element
+        .removeClass('collapsing')
+        .addClass('collapse in')[dimension]('')
+      this.transitioning = 0
+      this.$element
+        .trigger('shown.bs.collapse')
+    }
+
+    if (!$.support.transition) return complete.call(this)
+
+    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+    this.$element
+      .one('bsTransitionEnd', $.proxy(complete, this))
+      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
+  }
+
+  Collapse.prototype.hide = function () {
+    if (this.transitioning || !this.$element.hasClass('in')) return
+
+    var startEvent = $.Event('hide.bs.collapse')
+    this.$element.trigger(startEvent)
+    if (startEvent.isDefaultPrevented()) return
+
+    var dimension = this.dimension()
+
+    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+
+    this.$element
+      .addClass('collapsing')
+      .removeClass('collapse in')
+      .attr('aria-expanded', false)
+
+    this.$trigger
+      .addClass('collapsed')
+      .attr('aria-expanded', false)
+
+    this.transitioning = 1
+
+    var complete = function () {
+      this.transitioning = 0
+      this.$element
+        .removeClass('collapsing')
+        .addClass('collapse')
+        .trigger('hidden.bs.collapse')
+    }
+
+    if (!$.support.transition) return complete.call(this)
+
+    this.$element
+      [dimension](0)
+      .one('bsTransitionEnd', $.proxy(complete, this))
+      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+  }
+
+  Collapse.prototype.toggle = function () {
+    this[this.$element.hasClass('in') ? 'hide' : 'show']()
+  }
+
+  Collapse.prototype.getParent = function () {
+    return $(this.options.parent)
+      .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
+      .each($.proxy(function (i, element) {
+        var $element = $(element)
+        this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
+      }, this))
+      .end()
+  }
+
+  Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
+    var isOpen = $element.hasClass('in')
+
+    $element.attr('aria-expanded', isOpen)
+    $trigger
+      .toggleClass('collapsed', !isOpen)
+      .attr('aria-expanded', isOpen)
+  }
+
+  function getTargetFromTrigger($trigger) {
+    var href
+    var target = $trigger.attr('data-target')
+      || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+
+    return $(target)
+  }
+
+
+  // COLLAPSE PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.collapse')
+      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+      if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
+      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.collapse
+
+  $.fn.collapse             = Plugin
+  $.fn.collapse.Constructor = Collapse
+
+
+  // COLLAPSE NO CONFLICT
+  // ====================
+
+  $.fn.collapse.noConflict = function () {
+    $.fn.collapse = old
+    return this
+  }
+
+
+  // COLLAPSE DATA-API
+  // =================
+
+  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
+    var $this   = $(this)
+
+    if (!$this.attr('data-target')) e.preventDefault()
+
+    var $target = getTargetFromTrigger($this)
+    var data    = $target.data('bs.collapse')
+    var option  = data ? 'toggle' : $this.data()
+
+    Plugin.call($target, option)
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.3.5
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // DROPDOWN CLASS DEFINITION
+  // =========================
+
+  var backdrop = '.dropdown-backdrop'
+  var toggle   = '[data-toggle="dropdown"]'
+  var Dropdown = function (element) {
+    $(element).on('click.bs.dropdown', this.toggle)
+  }
+
+  Dropdown.VERSION = '3.3.5'
+
+  function getParent($this) {
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    var $parent = selector && $(selector)
+
+    return $parent && $parent.length ? $parent : $this.parent()
+  }
+
+  function clearMenus(e) {
+    if (e && e.which === 3) return
+    $(backdrop).remove()
+    $(toggle).each(function () {
+      var $this         = $(this)
+      var $parent       = getParent($this)
+      var relatedTarget = { relatedTarget: this }
+
+      if (!$parent.hasClass('open')) return
+
+      if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
+
+      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $this.attr('aria-expanded', 'false')
+      $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+    })
+  }
+
+  Dropdown.prototype.toggle = function (e) {
+    var $this = $(this)
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    clearMenus()
+
+    if (!isActive) {
+      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+        // if mobile we use a backdrop because click events don't delegate
+        $(document.createElement('div'))
+          .addClass('dropdown-backdrop')
+          .insertAfter($(this))
+          .on('click', clearMenus)
+      }
+
+      var relatedTarget = { relatedTarget: this }
+      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $this
+        .trigger('focus')
+        .attr('aria-expanded', 'true')
+
+      $parent
+        .toggleClass('open')
+        .trigger('shown.bs.dropdown', relatedTarget)
+    }
+
+    return false
+  }
+
+  Dropdown.prototype.keydown = function (e) {
+    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+
+    var $this = $(this)
+
+    e.preventDefault()
+    e.stopPropagation()
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    if (!isActive && e.which != 27 || isActive && e.which == 27) {
+      if (e.which == 27) $parent.find(toggle).trigger('focus')
+      return $this.trigger('click')
+    }
+
+    var desc = ' li:not(.disabled):visible a'
+    var $items = $parent.find('.dropdown-menu' + desc)
+
+    if (!$items.length) return
+
+    var index = $items.index(e.target)
+
+    if (e.which == 38 && index > 0)                 index--         // up
+    if (e.which == 40 && index < $items.length - 1) index++         // down
+    if (!~index)                                    index = 0
+
+    $items.eq(index).trigger('focus')
+  }
+
+
+  // DROPDOWN PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.dropdown')
+
+      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  var old = $.fn.dropdown
+
+  $.fn.dropdown             = Plugin
+  $.fn.dropdown.Constructor = Dropdown
+
+
+  // DROPDOWN NO CONFLICT
+  // ====================
+
+  $.fn.dropdown.noConflict = function () {
+    $.fn.dropdown = old
+    return this
+  }
+
+
+  // APPLY TO STANDARD DROPDOWN ELEMENTS
+  // ===================================
+
+  $(document)
+    .on('click.bs.dropdown.data-api', clearMenus)
+    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
+    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.3.5
+ * http://getbootstrap.com/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // MODAL CLASS DEFINITION
+  // ======================
+
+  var Modal = function (element, options) {
+    this.options             = options
+    this.$body               = $(document.body)
+    this.$element            = $(element)
+    this.$dialog             = this.$element.find('.modal-dialog')
+    this.$backdrop           = null
+    this.isShown             = null
+    this.originalBodyPad     = null
+    this.scrollbarWidth      = 0
+    this.ignoreBackdropClick = false
+
+    if (this.options.remote) {
+      this.$element
+        .find('.modal-content')
+        .load(this.options.remote, $.proxy(function () {
+          this.$element.trigger('loaded.bs.modal')
+        }, this))
+    }
+  }
+
+  Modal.VERSION  = '3.3.5'
+
+  Modal.TRANSITION_DURATION = 300
+  Modal.BACKDROP_TRANSITION_DURATION = 150
+
+  Modal.DEFAULTS = {
+    backdrop: true,
+    keyboard: true,
+    show: true
+  }
+
+  Modal.prototype.toggle = function (_relatedTarget) {
+    return this.isShown ? this.hide() : this.show(_relatedTarget)
+  }
+
+  Modal.prototype.show = function (_relatedTarget) {
+    var that = this
+    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+    this.$element.trigger(e)
+
+    if (this.isShown || e.isDefaultPrevented()) return
+
+    this.isShown = true
+
+    this.checkScrollbar()
+    this.setScrollbar()
+    this.$body.addClass('modal-open')
+
+    this.escape()
+    this.resize()
+
+    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
+      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
+        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
+      })
+    })
+
+    this.backdrop(function () {
+      var transition = $.support.transition && that.$element.hasClass('fade')
+
+      if (!that.$element.parent().length) {
+        that.$element.appendTo(that.$body) // don't move modals dom position
+      }
+
+      that.$element
+        .show()
+        .scrollTop(0)
+
+      that.adjustDialog()
+
+      if (transition) {
+        that.$element[0].offsetWidth // force reflow
+      }
+
+      that.$element.addClass('in')
+
+      that.enforceFocus()
+
+      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+      transition ?
+        that.$dialog // wait for modal to slide in
+          .one('bsTransitionEnd', function () {
+            that.$element.trigger('focus').trigger(e)
+          })
+          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+        that.$element.trigger('focus').trigger(e)
+    })
+  }
+
+  Modal.prototype.hide = function (e) {
+    if (e) e.preventDefault()
+
+    e = $.Event('hide.bs.modal')
+
+    this.$element.trigger(e)
+
+    if (!this.isShown || e.isDefaultPrevented()) return
+
+    this.isShown = false
+
+    this.escape()
+    this.resize()
+
+    $(document).off('focusin.bs.modal')
+
+    this.$element
+      .removeClass('in')
+      .off('click.dismiss.bs.modal')
+      .off('mouseup.dismiss.bs.modal')
+
+    this.$dialog.off('mousedown.dismiss.bs.modal')
+
+    $.support.transition && this.$element.hasClass('fade') ?
+      this.$element
+        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+      this.hideModal()
+  }
+
+  Modal.prototype.enforceFocus = function () {
+    $(document)
+      .off('focusin.bs.modal') // guard against infinite focus loop
+      .on('focusin.bs.modal', $.proxy(function (e) {
+        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+          this.$element.trigger('focus')
+        }
+      }, this))
+  }
+
+  Modal.prototype.escape = function () {
+    if (this.isShown && this.options.keyboard) {
+      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+        e.which == 27 && this.hide()
+      }, this))
+    } else if (!this.isShown) {
+      this.$element.off('keydown.dismiss.bs.modal')
+    }
+  }
+
+  Modal.prototype.resize = function () {
+    if (this.isShown) {
+      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
+    } else {
+      $(window).off('resize.bs.modal')
+    }
+  }
+
+  Modal.prototype.hideModal = function () {
+    var that = this
+    this.$element.hide()
+    this.backdrop(function () {
+      that.$body.removeClass('modal-open')
+      that.resetAdjustments()
+      that.resetScrollbar()
+      that.$element.trigger('hidden.bs.modal')
+    })
+  }
+
+  Modal.prototype.removeBackdrop = function () {
+    this.$backdrop && this.$backdrop.remove()
+    this.$backdrop = null
+  }
+
+  Modal.prototype.backdrop = function (callback) {
+    var that = this
+    var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+    if (this.isShown && this.options.backdrop) {
+      var doAnimate = $.support.transition && animate
+
+      this.$backdrop = $(document.createElement('div'))
+        .addClass('modal-backdrop ' + animate)
+        .appendTo(this.$body)
+
+      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+        if (this.ignoreBackdropClick) {
+          this.ignoreBackdropClick = false
+          return
+        }
+        if (e.target !== e.currentTarget) return
+        this.options.backdrop == 'static'
+          ? this.$element[0].focus()
+          : this.hide()
+      }, this))
+
+      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+      this.$backdrop.addClass('in')
+
+      if (!callback) return
+
+      doAnimate ?
+        this.$backdrop
+          .one('bsTransitionEnd', callback)
+          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+        callback()
+
+    } else if (!this.isShown && this.$backdrop) {
+      this.$backdrop.removeClass('in')
+
+      var callbackRemove = function () {
+        that.removeBackdrop()
+        callback && callback()
+      }
+      $.support.transition && this.$element.hasClass('fade') ?
+        this.$backdrop
+          .one('bsTransitionEnd', callbackRemove)
+          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+        callbackRemove()
+
+    } else if (callback) {
+      callback()
+    }
+  }
+
+  // these following methods are used to handle overflowing modals
+
+  Modal.prototype.handleUpdate = function () {
+    this.adjustDialog()
+  }
+
+  Modal.prototype.adjustDialog = function () {
+    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
+
+    this.$element.css({
+      paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
+      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
+    })
+  }
+
+  Modal.prototype.resetAdjustments = function () {
+    this.$element.css({
+      paddingLeft: '',
+      paddingRight: ''
+    })
+  }
+
+  Modal.prototype.checkScrollbar = function () {
+    var fullWindowWidth = window.innerWidth
+    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
+      var documentElementRect = document.documentElement.getBoundingClientRect()
+      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
+    }
+    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
+    this.scrollbarWidth = this.measureScrollbar()
+  }
+
+  Modal.prototype.setScrollbar = function () {
+    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+    this.originalBodyPad = document.body.style.paddingRight || ''
+    if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
+  }
+
+  Modal.prototype.resetScrollbar = function () {
+    this.$body.css('padding-right', this.originalBodyPad)
+  }
+
+  Modal.prototype.measureScrollbar = function () { // thx walsh
+    var scrollDiv = document.createElement('div')
+    scrollDiv.className = 'modal-scrollbar-measure'
+    this.$body.append(scrollDiv)
+    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+    this.$body[0].removeChild(scrollDiv)
+    return scrollbarWidth
+  }
+
+
+  // MODAL PLUGIN DEFINITION
+  // =======================
+
+  function Plugin(option, _relatedTarget) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.modal')
+      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+      if (typeof option == 'string') data[option](_relatedTarget)
+      else if (options.show) data.show(_relatedTarget)
+    })
+  }
+
+  var old = $.fn.modal
+
+  $.fn.modal             = Plugin
+  $.fn.modal.Constructor = Modal
+
+
+  // MODAL NO CONFLICT
+  // =================
+
+  $.fn.modal.noConflict = function () {
+    $.fn.modal = old
+    return this
+  }
+
+
+  // MODAL DATA-API
+  // ==============
+
+  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+    var $this   = $(this)
+    var href    = $this.attr('href')
+    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
+    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+    if ($this.is('a')) e.preventDefault()
+
+    $target.one('show.bs.modal', function (showEvent) {
+      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
+      $target.one('hidden.bs.modal', function () {
+        $this.is(':visible') && $this.trigger('focus')
+      })
+    })
+    Plugin.call($target, option, this)
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.3.5
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // TOOLTIP PUBLIC CLASS DEFINITION
+  // ===============================
+
+  var Tooltip = function (element, options) {
+    this.type       = null
+    this.options    = null
+    this.enabled    = null
+    this.timeout    = null
+    this.hoverState = null
+    this.$element   = null
+    this.inState    = null
+
+    this.init('tooltip', element, options)
+  }
+
+  Tooltip.VERSION  = '3.3.5'
+
+  Tooltip.TRANSITION_DURATION = 150
+
+  Tooltip.DEFAULTS = {
+    animation: true,
+    placement: 'top',
+    selector: false,
+    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+    trigger: 'hover focus',
+    title: '',
+    delay: 0,
+    html: false,
+    container: false,
+    viewport: {
+      selector: 'body',
+      padding: 0
+    }
+  }
+
+  Tooltip.prototype.init = function (type, element, options) {
+    this.enabled   = true
+    this.type      = type
+    this.$element  = $(element)
+    this.options   = this.getOptions(options)
+    this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
+    this.inState   = { click: false, hover: false, focus: false }
+
+    if (this.$element[0] instanceof document.constructor && !this.options.selector) {
+      throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
+    }
+
+    var triggers = this.options.trigger.split(' ')
+
+    for (var i = triggers.length; i--;) {
+      var trigger = triggers[i]
+
+      if (trigger == 'click') {
+        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+      } else if (trigger != 'manual') {
+        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
+        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+      }
+    }
+
+    this.options.selector ?
+      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+      this.fixTitle()
+  }
+
+  Tooltip.prototype.getDefaults = function () {
+    return Tooltip.DEFAULTS
+  }
+
+  Tooltip.prototype.getOptions = function (options) {
+    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+    if (options.delay && typeof options.delay == 'number') {
+      options.delay = {
+        show: options.delay,
+        hide: options.delay
+      }
+    }
+
+    return options
+  }
+
+  Tooltip.prototype.getDelegateOptions = function () {
+    var options  = {}
+    var defaults = this.getDefaults()
+
+    this._options && $.each(this._options, function (key, value) {
+      if (defaults[key] != value) options[key] = value
+    })
+
+    return options
+  }
+
+  Tooltip.prototype.enter = function (obj) {
+    var self = obj instanceof this.constructor ?
+      obj : $(obj.currentTarget).data('bs.' + this.type)
+
+    if (!self) {
+      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+      $(obj.currentTarget).data('bs.' + this.type, self)
+    }
+
+    if (obj instanceof $.Event) {
+      self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
+    }
+
+    if (self.tip().hasClass('in') || self.hoverState == 'in') {
+      self.hoverState = 'in'
+      return
+    }
+
+    clearTimeout(self.timeout)
+
+    self.hoverState = 'in'
+
+    if (!self.options.delay || !self.options.delay.show) return self.show()
+
+    self.timeout = setTimeout(function () {
+      if (self.hoverState == 'in') self.show()
+    }, self.options.delay.show)
+  }
+
+  Tooltip.prototype.isInStateTrue = function () {
+    for (var key in this.inState) {
+      if (this.inState[key]) return true
+    }
+
+    return false
+  }
+
+  Tooltip.prototype.leave = function (obj) {
+    var self = obj instanceof this.constructor ?
+      obj : $(obj.currentTarget).data('bs.' + this.type)
+
+    if (!self) {
+      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+      $(obj.currentTarget).data('bs.' + this.type, self)
+    }
+
+    if (obj instanceof $.Event) {
+      self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
+    }
+
+    if (self.isInStateTrue()) return
+
+    clearTimeout(self.timeout)
+
+    self.hoverState = 'out'
+
+    if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+    self.timeout = setTimeout(function () {
+      if (self.hoverState == 'out') self.hide()
+    }, self.options.delay.hide)
+  }
+
+  Tooltip.prototype.show = function () {
+    var e = $.Event('show.bs.' + this.type)
+
+    if (this.hasContent() && this.enabled) {
+      this.$element.trigger(e)
+
+      var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
+      if (e.isDefaultPrevented() || !inDom) return
+      var that = this
+
+      var $tip = this.tip()
+
+      var tipId = this.getUID(this.type)
+
+      this.setContent()
+      $tip.attr('id', tipId)
+      this.$element.attr('aria-describedby', tipId)
+
+      if (this.options.animation) $tip.addClass('fade')
+
+      var placement = typeof this.options.placement == 'function' ?
+        this.options.placement.call(this, $tip[0], this.$element[0]) :
+        this.options.placement
+
+      var autoToken = /\s?auto?\s?/i
+      var autoPlace = autoToken.test(placement)
+      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+      $tip
+        .detach()
+        .css({ top: 0, left: 0, display: 'block' })
+        .addClass(placement)
+        .data('bs.' + this.type, this)
+
+      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+      this.$element.trigger('inserted.bs.' + this.type)
+
+      var pos          = this.getPosition()
+      var actualWidth  = $tip[0].offsetWidth
+      var actualHeight = $tip[0].offsetHeight
+
+      if (autoPlace) {
+        var orgPlacement = placement
+        var viewportDim = this.getPosition(this.$viewport)
+
+        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :
+                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :
+                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :
+                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :
+                    placement
+
+        $tip
+          .removeClass(orgPlacement)
+          .addClass(placement)
+      }
+
+      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+      this.applyPlacement(calculatedOffset, placement)
+
+      var complete = function () {
+        var prevHoverState = that.hoverState
+        that.$element.trigger('shown.bs.' + that.type)
+        that.hoverState = null
+
+        if (prevHoverState == 'out') that.leave(that)
+      }
+
+      $.support.transition && this.$tip.hasClass('fade') ?
+        $tip
+          .one('bsTransitionEnd', complete)
+          .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+        complete()
+    }
+  }
+
+  Tooltip.prototype.applyPlacement = function (offset, placement) {
+    var $tip   = this.tip()
+    var width  = $tip[0].offsetWidth
+    var height = $tip[0].offsetHeight
+
+    // manually read margins because getBoundingClientRect includes difference
+    var marginTop = parseInt($tip.css('margin-top'), 10)
+    var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+    // we must check for NaN for ie 8/9
+    if (isNaN(marginTop))  marginTop  = 0
+    if (isNaN(marginLeft)) marginLeft = 0
+
+    offset.top  += marginTop
+    offset.left += marginLeft
+
+    // $.fn.offset doesn't round pixel values
+    // so we use setOffset directly with our own function B-0
+    $.offset.setOffset($tip[0], $.extend({
+      using: function (props) {
+        $tip.css({
+          top: Math.round(props.top),
+          left: Math.round(props.left)
+        })
+      }
+    }, offset), 0)
+
+    $tip.addClass('in')
+
+    // check to see if placing tip in new offset caused the tip to resize itself
+    var actualWidth  = $tip[0].offsetWidth
+    var actualHeight = $tip[0].offsetHeight
+
+    if (placement == 'top' && actualHeight != height) {
+      offset.top = offset.top + height - actualHeight
+    }
+
+    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+    if (delta.left) offset.left += delta.left
+    else offset.top += delta.top
+
+    var isVertical          = /top|bottom/.test(placement)
+    var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+    var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
+
+    $tip.offset(offset)
+    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
+  }
+
+  Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
+    this.arrow()
+      .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
+      .css(isVertical ? 'top' : 'left', '')
+  }
+
+  Tooltip.prototype.setContent = function () {
+    var $tip  = this.tip()
+    var title = this.getTitle()
+
+    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+    $tip.removeClass('fade in top bottom left right')
+  }
+
+  Tooltip.prototype.hide = function (callback) {
+    var that = this
+    var $tip = $(this.$tip)
+    var e    = $.Event('hide.bs.' + this.type)
+
+    function complete() {
+      if (that.hoverState != 'in') $tip.detach()
+      that.$element
+        .removeAttr('aria-describedby')
+        .trigger('hidden.bs.' + that.type)
+      callback && callback()
+    }
+
+    this.$element.trigger(e)
+
+    if (e.isDefaultPrevented()) return
+
+    $tip.removeClass('in')
+
+    $.support.transition && $tip.hasClass('fade') ?
+      $tip
+        .one('bsTransitionEnd', complete)
+        .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+      complete()
+
+    this.hoverState = null
+
+    return this
+  }
+
+  Tooltip.prototype.fixTitle = function () {
+    var $e = this.$element
+    if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
+      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+    }
+  }
+
+  Tooltip.prototype.hasContent = function () {
+    return this.getTitle()
+  }
+
+  Tooltip.prototype.getPosition = function ($element) {
+    $element   = $element || this.$element
+
+    var el     = $element[0]
+    var isBody = el.tagName == 'BODY'
+
+    var elRect    = el.getBoundingClientRect()
+    if (elRect.width == null) {
+      // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
+      elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+    }
+    var elOffset  = isBody ? { top: 0, left: 0 } : $element.offset()
+    var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
+    var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
+
+    return $.extend({}, elRect, scroll, outerDims, elOffset)
+  }
+
+  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2 } :
+           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+
+  }
+
+  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
+    var delta = { top: 0, left: 0 }
+    if (!this.$viewport) return delta
+
+    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
+    var viewportDimensions = this.getPosition(this.$viewport)
+
+    if (/right|left/.test(placement)) {
+      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
+      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
+      if (topEdgeOffset < viewportDimensions.top) { // top overflow
+        delta.top = viewportDimensions.top - topEdgeOffset
+      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+        delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+      }
+    } else {
+      var leftEdgeOffset  = pos.left - viewportPadding
+      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
+      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+        delta.left = viewportDimensions.left - leftEdgeOffset
+      } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
+        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+      }
+    }
+
+    return delta
+  }
+
+  Tooltip.prototype.getTitle = function () {
+    var title
+    var $e = this.$element
+    var o  = this.options
+
+    title = $e.attr('data-original-title')
+      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
+
+    return title
+  }
+
+  Tooltip.prototype.getUID = function (prefix) {
+    do prefix += ~~(Math.random() * 1000000)
+    while (document.getElementById(prefix))
+    return prefix
+  }
+
+  Tooltip.prototype.tip = function () {
+    if (!this.$tip) {
+      this.$tip = $(this.options.template)
+      if (this.$tip.length != 1) {
+        throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
+      }
+    }
+    return this.$tip
+  }
+
+  Tooltip.prototype.arrow = function () {
+    return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+  }
+
+  Tooltip.prototype.enable = function () {
+    this.enabled = true
+  }
+
+  Tooltip.prototype.disable = function () {
+    this.enabled = false
+  }
+
+  Tooltip.prototype.toggleEnabled = function () {
+    this.enabled = !this.enabled
+  }
+
+  Tooltip.prototype.toggle = function (e) {
+    var self = this
+    if (e) {
+      self = $(e.currentTarget).data('bs.' + this.type)
+      if (!self) {
+        self = new this.constructor(e.currentTarget, this.getDelegateOptions())
+        $(e.currentTarget).data('bs.' + this.type, self)
+      }
+    }
+
+    if (e) {
+      self.inState.click = !self.inState.click
+      if (self.isInStateTrue()) self.enter(self)
+      else self.leave(self)
+    } else {
+      self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+    }
+  }
+
+  Tooltip.prototype.destroy = function () {
+    var that = this
+    clearTimeout(this.timeout)
+    this.hide(function () {
+      that.$element.off('.' + that.type).removeData('bs.' + that.type)
+      if (that.$tip) {
+        that.$tip.detach()
+      }
+      that.$tip = null
+      that.$arrow = null
+      that.$viewport = null
+    })
+  }
+
+
+  // TOOLTIP PLUGIN DEFINITION
+  // =========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.tooltip')
+      var options = typeof option == 'object' && option
+
+      if (!data && /destroy|hide/.test(option)) return
+      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.tooltip
+
+  $.fn.tooltip             = Plugin
+  $.fn.tooltip.Constructor = Tooltip
+
+
+  // TOOLTIP NO CONFLICT
+  // ===================
+
+  $.fn.tooltip.noConflict = function () {
+    $.fn.tooltip = old
+    return this
+  }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.3.5
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // POPOVER PUBLIC CLASS DEFINITION
+  // ===============================
+
+  var Popover = function (element, options) {
+    this.init('popover', element, options)
+  }
+
+  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+  Popover.VERSION  = '3.3.5'
+
+  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+    placement: 'right',
+    trigger: 'click',
+    content: '',
+    template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+  })
+
+
+  // NOTE: POPOVER EXTENDS tooltip.js
+  // ================================
+
+  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+  Popover.prototype.constructor = Popover
+
+  Popover.prototype.getDefaults = function () {
+    return Popover.DEFAULTS
+  }
+
+  Popover.prototype.setContent = function () {
+    var $tip    = this.tip()
+    var title   = this.getTitle()
+    var content = this.getContent()
+
+    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+    $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
+      this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+    ](content)
+
+    $tip.removeClass('fade top bottom left right in')
+
+    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+    // this manually by checking the contents.
+    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+  }
+
+  Popover.prototype.hasContent = function () {
+    return this.getTitle() || this.getContent()
+  }
+
+  Popover.prototype.getContent = function () {
+    var $e = this.$element
+    var o  = this.options
+
+    return $e.attr('data-content')
+      || (typeof o.content == 'function' ?
+            o.content.call($e[0]) :
+            o.content)
+  }
+
+  Popover.prototype.arrow = function () {
+    return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
+  }
+
+
+  // POPOVER PLUGIN DEFINITION
+  // =========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.popover')
+      var options = typeof option == 'object' && option
+
+      if (!data && /destroy|hide/.test(option)) return
+      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.popover
+
+  $.fn.popover             = Plugin
+  $.fn.popover.Constructor = Popover
+
+
+  // POPOVER NO CONFLICT
+  // ===================
+
+  $.fn.popover.noConflict = function () {
+    $.fn.popover = old
+    return this
+  }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.3.5
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // SCROLLSPY CLASS DEFINITION
+  // ==========================
+
+  function ScrollSpy(element, options) {
+    this.$body          = $(document.body)
+    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
+    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
+    this.selector       = (this.options.target || '') + ' .nav li > a'
+    this.offsets        = []
+    this.targets        = []
+    this.activeTarget   = null
+    this.scrollHeight   = 0
+
+    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
+    this.refresh()
+    this.process()
+  }
+
+  ScrollSpy.VERSION  = '3.3.5'
+
+  ScrollSpy.DEFAULTS = {
+    offset: 10
+  }
+
+  ScrollSpy.prototype.getScrollHeight = function () {
+    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
+  }
+
+  ScrollSpy.prototype.refresh = function () {
+    var that          = this
+    var offsetMethod  = 'offset'
+    var offsetBase    = 0
+
+    this.offsets      = []
+    this.targets      = []
+    this.scrollHeight = this.getScrollHeight()
+
+    if (!$.isWindow(this.$scrollElement[0])) {
+      offsetMethod = 'position'
+      offsetBase   = this.$scrollElement.scrollTop()
+    }
+
+    this.$body
+      .find(this.selector)
+      .map(function () {
+        var $el   = $(this)
+        var href  = $el.data('target') || $el.attr('href')
+        var $href = /^#./.test(href) && $(href)
+
+        return ($href
+          && $href.length
+          && $href.is(':visible')
+          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
+      })
+      .sort(function (a, b) { return a[0] - b[0] })
+      .each(function () {
+        that.offsets.push(this[0])
+        that.targets.push(this[1])
+      })
+  }
+
+  ScrollSpy.prototype.process = function () {
+    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
+    var scrollHeight = this.getScrollHeight()
+    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
+    var offsets      = this.offsets
+    var targets      = this.targets
+    var activeTarget = this.activeTarget
+    var i
+
+    if (this.scrollHeight != scrollHeight) {
+      this.refresh()
+    }
+
+    if (scrollTop >= maxScroll) {
+      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
+    }
+
+    if (activeTarget && scrollTop < offsets[0]) {
+      this.activeTarget = null
+      return this.clear()
+    }
+
+    for (i = offsets.length; i--;) {
+      activeTarget != targets[i]
+        && scrollTop >= offsets[i]
+        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
+        && this.activate(targets[i])
+    }
+  }
+
+  ScrollSpy.prototype.activate = function (target) {
+    this.activeTarget = target
+
+    this.clear()
+
+    var selector = this.selector +
+      '[data-target="' + target + '"],' +
+      this.selector + '[href="' + target + '"]'
+
+    var active = $(selector)
+      .parents('li')
+      .addClass('active')
+
+    if (active.parent('.dropdown-menu').length) {
+      active = active
+        .closest('li.dropdown')
+        .addClass('active')
+    }
+
+    active.trigger('activate.bs.scrollspy')
+  }
+
+  ScrollSpy.prototype.clear = function () {
+    $(this.selector)
+      .parentsUntil(this.options.target, '.active')
+      .removeClass('active')
+  }
+
+
+  // SCROLLSPY PLUGIN DEFINITION
+  // ===========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.scrollspy')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.scrollspy
+
+  $.fn.scrollspy             = Plugin
+  $.fn.scrollspy.Constructor = ScrollSpy
+
+
+  // SCROLLSPY NO CONFLICT
+  // =====================
+
+  $.fn.scrollspy.noConflict = function () {
+    $.fn.scrollspy = old
+    return this
+  }
+
+
+  // SCROLLSPY DATA-API
+  // ==================
+
+  $(window).on('load.bs.scrollspy.data-api', function () {
+    $('[data-spy="scroll"]').each(function () {
+      var $spy = $(this)
+      Plugin.call($spy, $spy.data())
+    })
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tab.js v3.3.5
+ * http://getbootstrap.com/javascript/#tabs
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // TAB CLASS DEFINITION
+  // ====================
+
+  var Tab = function (element) {
+    // jscs:disable requireDollarBeforejQueryAssignment
+    this.element = $(element)
+    // jscs:enable requireDollarBeforejQueryAssignment
+  }
+
+  Tab.VERSION = '3.3.5'
+
+  Tab.TRANSITION_DURATION = 150
+
+  Tab.prototype.show = function () {
+    var $this    = this.element
+    var $ul      = $this.closest('ul:not(.dropdown-menu)')
+    var selector = $this.data('target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    if ($this.parent('li').hasClass('active')) return
+
+    var $previous = $ul.find('.active:last a')
+    var hideEvent = $.Event('hide.bs.tab', {
+      relatedTarget: $this[0]
+    })
+    var showEvent = $.Event('show.bs.tab', {
+      relatedTarget: $previous[0]
+    })
+
+    $previous.trigger(hideEvent)
+    $this.trigger(showEvent)
+
+    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+
+    var $target = $(selector)
+
+    this.activate($this.closest('li'), $ul)
+    this.activate($target, $target.parent(), function () {
+      $previous.trigger({
+        type: 'hidden.bs.tab',
+        relatedTarget: $this[0]
+      })
+      $this.trigger({
+        type: 'shown.bs.tab',
+        relatedTarget: $previous[0]
+      })
+    })
+  }
+
+  Tab.prototype.activate = function (element, container, callback) {
+    var $active    = container.find('> .active')
+    var transition = callback
+      && $.support.transition
+      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
+
+    function next() {
+      $active
+        .removeClass('active')
+        .find('> .dropdown-menu > .active')
+          .removeClass('active')
+        .end()
+        .find('[data-toggle="tab"]')
+          .attr('aria-expanded', false)
+
+      element
+        .addClass('active')
+        .find('[data-toggle="tab"]')
+          .attr('aria-expanded', true)
+
+      if (transition) {
+        element[0].offsetWidth // reflow for transition
+        element.addClass('in')
+      } else {
+        element.removeClass('fade')
+      }
+
+      if (element.parent('.dropdown-menu').length) {
+        element
+          .closest('li.dropdown')
+            .addClass('active')
+          .end()
+          .find('[data-toggle="tab"]')
+            .attr('aria-expanded', true)
+      }
+
+      callback && callback()
+    }
+
+    $active.length && transition ?
+      $active
+        .one('bsTransitionEnd', next)
+        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
+      next()
+
+    $active.removeClass('in')
+  }
+
+
+  // TAB PLUGIN DEFINITION
+  // =====================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.tab')
+
+      if (!data) $this.data('bs.tab', (data = new Tab(this)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.tab
+
+  $.fn.tab             = Plugin
+  $.fn.tab.Constructor = Tab
+
+
+  // TAB NO CONFLICT
+  // ===============
+
+  $.fn.tab.noConflict = function () {
+    $.fn.tab = old
+    return this
+  }
+
+
+  // TAB DATA-API
+  // ============
+
+  var clickHandler = function (e) {
+    e.preventDefault()
+    Plugin.call($(this), 'show')
+  }
+
+  $(document)
+    .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
+    .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: affix.js v3.3.5
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // AFFIX CLASS DEFINITION
+  // ======================
+
+  var Affix = function (element, options) {
+    this.options = $.extend({}, Affix.DEFAULTS, options)
+
+    this.$target = $(this.options.target)
+      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
+
+    this.$element     = $(element)
+    this.affixed      = null
+    this.unpin        = null
+    this.pinnedOffset = null
+
+    this.checkPosition()
+  }
+
+  Affix.VERSION  = '3.3.5'
+
+  Affix.RESET    = 'affix affix-top affix-bottom'
+
+  Affix.DEFAULTS = {
+    offset: 0,
+    target: window
+  }
+
+  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
+    var scrollTop    = this.$target.scrollTop()
+    var position     = this.$element.offset()
+    var targetHeight = this.$target.height()
+
+    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
+
+    if (this.affixed == 'bottom') {
+      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
+      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
+    }
+
+    var initializing   = this.affixed == null
+    var colliderTop    = initializing ? scrollTop : position.top
+    var colliderHeight = initializing ? targetHeight : height
+
+    if (offsetTop != null && scrollTop <= offsetTop) return 'top'
+    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
+
+    return false
+  }
+
+  Affix.prototype.getPinnedOffset = function () {
+    if (this.pinnedOffset) return this.pinnedOffset
+    this.$element.removeClass(Affix.RESET).addClass('affix')
+    var scrollTop = this.$target.scrollTop()
+    var position  = this.$element.offset()
+    return (this.pinnedOffset = position.top - scrollTop)
+  }
+
+  Affix.prototype.checkPositionWithEventLoop = function () {
+    setTimeout($.proxy(this.checkPosition, this), 1)
+  }
+
+  Affix.prototype.checkPosition = function () {
+    if (!this.$element.is(':visible')) return
+
+    var height       = this.$element.height()
+    var offset       = this.options.offset
+    var offsetTop    = offset.top
+    var offsetBottom = offset.bottom
+    var scrollHeight = Math.max($(document).height(), $(document.body).height())
+
+    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
+    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
+    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+
+    if (this.affixed != affix) {
+      if (this.unpin != null) this.$element.css('top', '')
+
+      var affixType = 'affix' + (affix ? '-' + affix : '')
+      var e         = $.Event(affixType + '.bs.affix')
+
+      this.$element.trigger(e)
+
+      if (e.isDefaultPrevented()) return
+
+      this.affixed = affix
+      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+      this.$element
+        .removeClass(Affix.RESET)
+        .addClass(affixType)
+        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
+    }
+
+    if (affix == 'bottom') {
+      this.$element.offset({
+        top: scrollHeight - height - offsetBottom
+      })
+    }
+  }
+
+
+  // AFFIX PLUGIN DEFINITION
+  // =======================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.affix')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.affix
+
+  $.fn.affix             = Plugin
+  $.fn.affix.Constructor = Affix
+
+
+  // AFFIX NO CONFLICT
+  // =================
+
+  $.fn.affix.noConflict = function () {
+    $.fn.affix = old
+    return this
+  }
+
+
+  // AFFIX DATA-API
+  // ==============
+
+  $(window).on('load', function () {
+    $('[data-spy="affix"]').each(function () {
+      var $spy = $(this)
+      var data = $spy.data()
+
+      data.offset = data.offset || {}
+
+      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
+      if (data.offsetTop    != null) data.offset.top    = data.offsetTop
+
+      Plugin.call($spy, data)
+    })
+  })
+
+}(jQuery);
diff --git a/web/root/bootstrap/js/bootstrap.min.js b/web/root/bootstrap/js/bootstrap.min.js
new file mode 100644
index 0000000000..133aeecb98
--- /dev/null
+++ b/web/root/bootstrap/js/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),
+d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
diff --git a/web/root/bootstrap/js/npm.js b/web/root/bootstrap/js/npm.js
new file mode 100644
index 0000000000..bf6aa80602
--- /dev/null
+++ b/web/root/bootstrap/js/npm.js
@@ -0,0 +1,13 @@
+// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
+require('../../js/transition.js')
+require('../../js/alert.js')
+require('../../js/button.js')
+require('../../js/carousel.js')
+require('../../js/collapse.js')
+require('../../js/dropdown.js')
+require('../../js/modal.js')
+require('../../js/tooltip.js')
+require('../../js/popover.js')
+require('../../js/scrollspy.js')
+require('../../js/tab.js')
+require('../../js/affix.js')
\ No newline at end of file
diff --git a/web/root/css/navbar-fixed-top.css b/web/root/css/navbar-fixed-top.css
new file mode 100644
index 0000000000..383ca8d4ce
--- /dev/null
+++ b/web/root/css/navbar-fixed-top.css
@@ -0,0 +1,9 @@
+body {
+	min-height: 2000px;
+	padding-top: 70px;
+}
+
+.dropdown-menu {
+	max-height: 500px;
+	overflow-y: auto;
+}
\ No newline at end of file
diff --git a/web/root/css/offcanvas.css b/web/root/css/offcanvas.css
new file mode 100644
index 0000000000..40f97c4c20
--- /dev/null
+++ b/web/root/css/offcanvas.css
@@ -0,0 +1,59 @@
+/*
+ * Style tweaks
+ * --------------------------------------------------
+ */
+html,
+body {
+	overflow-x: hidden; /* Prevent scroll on narrow devices */
+}
+body {
+	padding-top: 70px;
+}
+footer {
+	padding: 30px 0;
+}
+
+/*
+ * Off Canvas
+ * --------------------------------------------------
+ */
+@media screen and (max-width: 767px) {
+  .row-offcanvas {
+	position: relative;
+	-webkit-transition: all .25s ease-out;
+		 -o-transition: all .25s ease-out;
+			transition: all .25s ease-out;
+  }
+
+  .row-offcanvas-right {
+	right: 0;
+  }
+
+  .row-offcanvas-left {
+	left: 0;
+  }
+
+  .row-offcanvas-right
+  .sidebar-offcanvas {
+	right: -50%; /* 6 columns */
+  }
+
+  .row-offcanvas-left
+  .sidebar-offcanvas {
+	left: -50%; /* 6 columns */
+  }
+
+  .row-offcanvas-right.active {
+	right: 50%; /* 6 columns */
+  }
+
+  .row-offcanvas-left.active {
+	left: 50%; /* 6 columns */
+  }
+
+  .sidebar-offcanvas {
+	position: absolute;
+	top: 0;
+	width: 50%; /* 6 columns */
+  }
+}
\ No newline at end of file
diff --git a/web/root/css/style.css b/web/root/css/style.css
new file mode 100644
index 0000000000..ea31845b02
--- /dev/null
+++ b/web/root/css/style.css
@@ -0,0 +1,64 @@
+a.list-group-item:hover {
+	background-color: #A9E2F3;
+}
+
+a.list-group-item.scanned:hover {
+}
+
+a.list-group-item.active {
+	background-color: #A9E2F3;
+}
+
+a.list-group-item.scanned:hover {
+}
+
+a.unread {
+	background: #FFFFFF;
+}
+
+a.read {
+	background: #E6E6E6;
+}
+
+blockquote {
+	font-size: 14px;
+	margin: 7px;
+	padding: 7px;
+	border-left: 5px solid #A4A4A4;
+}
+
+div.message {
+	margin: 1em 0 1em 1em;
+}
+
+input.dropdown {
+	clear: both;
+	margin: 0 0 1em 0;
+}
+
+li.list-group-item.mail:hover {
+	background-color: #A9E2F3;
+}
+
+span.message-header.unread {
+	background-color: #FCF8E3;
+}
+
+span.badge.scanned {
+	background-color: #2E9AFE;
+}
+
+span.badge.total {
+}
+
+.icon {
+	margin: 0 1em 1em 0;
+}
+
+.striped:nth-of-type(even) {
+	background: #F2F2F2;
+}
+
+.reply {
+	margin: 1em 0 1em 0;
+}
\ No newline at end of file
diff --git a/web/root/error/400.html b/web/root/error/400.html
new file mode 100644
index 0000000000..db9d38c8f9
--- /dev/null
+++ b/web/root/error/400.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 400.html,v 1.3 2004/10/27 05:57:42 deuce Exp $ -->
+<TITLE>Bad Request</TITLE>
+</HEAD>
+<BODY>
+Your browser has sent a request that this server can not understand
+</BODY>
+</HTML>
diff --git a/web/root/error/401.html b/web/root/error/401.html
new file mode 100644
index 0000000000..42265595fa
--- /dev/null
+++ b/web/root/error/401.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 401.html,v 1.3 2004/10/27 05:57:42 deuce Exp $ -->
+<TITLE>Unauthorized</TITLE>
+</HEAD>
+<BODY>
+You are not authorized to view this page
+</BODY>
+</HTML>
diff --git a/web/root/error/403.html b/web/root/error/403.html
new file mode 100644
index 0000000000..e5db33f065
--- /dev/null
+++ b/web/root/error/403.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 403.html,v 1.3 2004/10/27 05:57:42 deuce Exp $ -->
+<TITLE>Forbiden</TITLE>
+</HEAD>
+<BODY>
+You are forbidden from viewing this page
+</BODY>
+</HTML>
diff --git a/web/root/error/404.html b/web/root/error/404.html
new file mode 100644
index 0000000000..dddbf324c9
--- /dev/null
+++ b/web/root/error/404.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<!-- $Id: 404.html,v 1.4 2010/02/22 23:03:25 rswindell Exp $ -->
+<title>404 File not found!</title>
+<body>
+No resource at this location.
+</body>
+</html>
diff --git a/web/root/error/414.html b/web/root/error/414.html
new file mode 100644
index 0000000000..cb262b31b9
--- /dev/null
+++ b/web/root/error/414.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 414.html,v 1.3 2004/10/27 05:57:42 deuce Exp $ -->
+<TITLE>Request URI Too Long</TITLE>
+</HEAD>
+<BODY>
+The request URI was too long
+</BODY>
+</HTML>
diff --git a/web/root/error/416.html b/web/root/error/416.html
new file mode 100644
index 0000000000..c31fe1df8a
--- /dev/null
+++ b/web/root/error/416.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 416.html,v 1.1 2006/08/08 04:48:45 deuce Exp $ -->
+<TITLE>Requested Range Unsatisfiable</TITLE>
+</HEAD>
+<BODY>
+Your browser requested a range which cannot be satisfied.
+</BODY>
+</HTML>
diff --git a/web/root/error/500.html b/web/root/error/500.html
new file mode 100644
index 0000000000..52d797f64a
--- /dev/null
+++ b/web/root/error/500.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 500.html,v 1.4 2005/09/12 00:15:19 deuce Exp $ -->
+<TITLE>Internal Server Error</TITLE>
+</HEAD>
+<BODY>
+Internal Server Error!
+</BODY>
+</HTML>
diff --git a/web/root/error/501.html b/web/root/error/501.html
new file mode 100644
index 0000000000..c9b3afb035
--- /dev/null
+++ b/web/root/error/501.html
@@ -0,0 +1,9 @@
+<HTML>
+<HEAD>
+<!-- $Id: 501.html,v 1.3 2004/10/27 05:57:42 deuce Exp $ -->
+<TITLE>Not Implemented</TITLE>
+</HEAD>
+<BODY>
+Support for the type of request has not yet been added to Synchronet
+</BODY>
+</HTML>
diff --git a/web/root/images/favicon.ico b/web/root/images/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..bcf9f091444f146b69a3da4e0eeb6f35fb84baf0
GIT binary patch
literal 3774
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
bME?NMpfHk$1m-_}2>l<*|34qf$3g)Bs9H-!

literal 0
HcmV?d00001

diff --git a/web/root/index.xjs b/web/root/index.xjs
new file mode 100644
index 0000000000..9357428e9e
--- /dev/null
+++ b/web/root/index.xjs
@@ -0,0 +1,200 @@
+<?xjs
+	load('xjs.js');
+	load(system.exec_dir + "../web/lib/init.js");
+	load(settings.web_lib + "auth.js");
+	load(settings.web_lib + "pages.js");
+	load(settings.web_lib + "sidebar.js");
+
+	if(	typeof http_request.query.page == "undefined"
+		||
+		!file_exists(settings.web_root + "pages/" + file_getname(http_request.query.page[0]))
+	) {
+		var page = "000-home.xjs";
+	} else {
+		var page = file_getname(http_request.query.page[0]);
+	}
+?>
+
+
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="description" content="">
+		<meta name="author" content="">
+		<link rel="icon" href="./images/favicon.ico">
+		<title><?xjs write(system.name); ?></title>
+		<link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
+		<link href="./css/offcanvas.css" rel="stylesheet">
+		<link href="./css/style.css" rel="stylesheet">
+	</head>
+
+	<body>
+
+		<script src="./js/jquery.min.js"></script>
+		<script src="./bootstrap/js/bootstrap.min.js"></script>
+		<script src="./js/common.js"></script>
+
+		<div class="modal" id="popUpModal" tabindex="-1" role="dialog" style="display:none;">
+			<div class="modal-dialog" role="document">
+				<div class="modal-content">
+					<div class="modal-header">
+						<h4 class="modal-title" id="popUpModalTitle">Pop-Up Thingie</h4>
+					</div>
+					<div class="modal-body" id="popUpModalBody"></div>
+					<div class="modal-footer">
+						<button type="button" class="btn btn-default" id="popUpModalCloseButton">Close</button>
+						<button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>Submit</button>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<nav class="navbar navbar-default navbar-fixed-top">
+			<div class="container">
+				<div class="navbar-header">
+					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+						<span class="sr-only">Sidebar</span>
+						<span class="icon-bar"></span>
+						<span class="icon-bar"></span>
+						<span class="icon-bar"></span>
+					</button>
+					<a class="navbar-brand" href="./"><?xjs write(system.name); ?></a>
+				</div>
+				<div id="navbar" class="collapse navbar-collapse">
+					<ul class="nav navbar-nav">
+						<?xjs 
+							getPages(true).forEach(
+								function(thePage) {
+									writeln(
+										'<li' +
+										(thePage.page == page ? ' class="active"' : "") + 
+										'>' +
+										'<a href="./?page=' + thePage.page + '">' +
+										thePage.title +
+										'</a></li>'
+									);
+								}
+							);
+							var more = getPages(false);
+							if(more.length > 0) {
+								writeln(
+									'<li class="dropdown">' +
+									'<a	href="#" ' +
+										'class="dropdown-toggle" ' +
+										'data-toggle="dropdown" ' +
+										'role="button" ' +
+										'aria-haspopup="true" ' +
+										'aria-expanded="false">' +
+									'More<span class="caret"></span></a>' +
+									'<ul class="dropdown-menu">'
+								);
+								more.forEach(
+									function(thePage) {
+										writeln(
+											'<li' +
+											(thePage.page == page ? ' class="active"' : "") + 
+											'>' +
+											'<a href="./?page=' + thePage.page + '">' +
+											thePage.title +
+											'</a></li>'
+										);
+									}
+								);
+								writeln('</ul></li>');
+							}
+						?>
+					</ul>
+					<ul class="nav navbar-nav navbar-right">
+					<?xjs
+					if(user.alias == settings.guest || user.number < 1) {
+						if(settings.user_registration)
+							writeln('<li><a href="./?page=000-register.xjs">Register</a></li>');
+						writeln(
+							'<li class="dropdown">' +
+							'<a	href="#" ' +
+								'class="dropdown-toggle" ' +
+								'data-toggle="dropdown" ' +
+								'role="button" ' +
+								'aria-haspopup="true" ' +
+								'aria-expanded="false">' +
+							'Log in<span class="caret"></span></a>' +
+							'<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom: 0px;">' +
+							'<form>' +
+							'<label for="input-username" class="sr-only">Username</label>' +
+							'<input id="input-username" title="Username" type="text" class="dropdown form-control" placeholder="Username">' +
+							'<label for="input-password" class="sr-only">Password</label>' +
+							'<input id="input-password" title="Password" type="password" class="dropdown form-control" placeholder="Password">' +
+							'<input id="button-login" class="dropdown btn btn-primary" type="submit" value="Log in">' +
+							'</form>' +
+							'</div>' +
+							'</li>'
+						);
+					} else {
+						writeln(
+							'<li class="dropdown">' +
+							'<a	href="#" ' +
+								'class="dropdown-toggle" ' +
+								'data-toggle="dropdown" ' +
+								'role="button" ' +
+								'aria-haspopup="true" ' +
+								'aria-expanded="false">' +
+								user.alias + 
+								' <span class="badge scanned" title="Unread mail" ' +
+								'id="badge-unread-mail"></span>' +
+								' <span class="caret"></span>' +
+							'</a>' +
+							'<ul class="dropdown-menu">' +
+							'<li><a href="./?page=000-mail.ssjs">Mail ' +
+							'<span class="badge scanned" title="Unread mail" id="badge-unread-mail-inner"></span>' +
+							'</a></li>' +
+							'<li><a id="button-logout" href="#">Log out</a></li>' +
+							'</ul>'
+						);
+					}
+					?>
+					</ul>
+				</div>
+		  </div>
+		</nav>
+
+		<div class="container">
+			
+			<div class="row row-offcanvas row-offcanvas-right">
+
+				<div class="col-xs-12 col-sm-9">
+					<p class="pull-right visible-xs">
+						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
+							<span class="glyphicon glyphicon-tasks"></span> Sidebar
+						</button>
+					</p>
+					<div class="row">
+					<?xjs 
+						var ini = getWebCtrl();
+						if((typeof ini == "boolean" && !ini) || webCtrlTest(ini, page))
+							write(getPage(page));
+					?>
+					</div>
+				</div>
+
+				<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
+					<?xjs writeSidebarModules(); ?>
+				</div>
+
+			</div>
+
+		  	<hr>
+			
+			<footer>
+			<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
+			</footer>
+
+		</div>
+
+		<script src="./js/offcanvas.js"></script>
+
+	</body>
+
+</html>
\ No newline at end of file
diff --git a/web/root/js/common.js b/web/root/js/common.js
new file mode 100644
index 0000000000..ff9b48fd6f
--- /dev/null
+++ b/web/root/js/common.js
@@ -0,0 +1,118 @@
+// How often to check for unread mail, new telegrams (milliseconds)
+var updateInterval = 60000;
+
+var login = function() {
+	if($("#input-username").val() == "" || $("#input-password").val() == "")
+		return;
+	$.ajax(
+		{	'url' : "./api/auth.ssjs",
+			'method' : "POST",
+			'data' : {
+				'username' : $("#input-username").val(),
+				'password' : $("#input-password").val()
+			}
+		}
+	).done(
+		function(data) {
+			if(data.authenticated)
+				window.location.reload();
+			else
+				$("#login-form").append('<p class="text-danger">Login failed</p>');
+		}
+	);
+}
+
+var logout = function() {
+	$.ajax(
+		{	'url' : "./api/auth.ssjs",
+			'method' : "GET",
+			'data' : {
+				'logout' : true
+			}
+		}
+	).done(
+		function(data) {
+			if(!data.authenticated)
+				window.location.reload();
+		}
+	);
+}
+
+var scrollUp = function() {
+	if(window.location.hash == "")
+		return;
+	if($('#navbar').length < 1)
+		return;
+	window.scrollBy(0, -document.getElementById('navbar').offsetHeight);
+}
+
+var getMailUnreadCount = function() {
+	$.getJSON(
+		"./api/forum.ssjs?call=get-mail-unread-count",
+		function(data) {
+			$("#badge-unread-mail").text(data.count < 1 ? "" : data.count);
+			$("#badge-unread-mail-inner").text(data.count < 1 ? "" : data.count);
+		}
+	);
+}
+
+var sendTelegram = function(alias) {
+	$('#popUpModalTitle').html("Send a telegram to " + alias);
+	$('#popUpModalBody').html('<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">');
+	$('#popUpModalActionButton').show();
+	$('#popUpModalActionButton').click(
+		function() {
+			$.getJSON(
+				'./api/system.ssjs?call=send-telegram&user=' + alias + '&telegram=' + $('#telegram').val(),
+				function(data) {}
+			);
+			$('#popUpModal').modal('hide');
+		}
+	);
+	$('#popUpModal').modal('show');
+}
+
+var getTelegram = function() {
+	$.getJSON(
+		'./api/system.ssjs?call=get-telegram',
+		function(data) {
+			if(data.telegram === null)
+				return;
+			var tg = data.telegram.replace(/\1./g, '').replace(/\r?\n/g, '<br>');
+			$('#popUpModalTitle').html("New telegram(s) received");
+			$('#popUpModalBody').append(tg);
+			$('#popUpModalActionButton').hide();
+			$('#popUpModal').modal('show');
+		}
+	);
+}
+
+window.onload =	function() { 
+
+	$("#button-logout").click(logout);
+	$("#button-login").click(login);
+
+	$('#popUpModal').on(
+		'hidden.bs.modal',
+		function(e) {
+			$('#popUpModalActionButton').off('click');
+			$('#popUpModalTitle').empty();
+			$('#popUpModalBody').empty();
+		}
+	);
+	$("#popUpModalCloseButton").click(
+		function() {
+			$('#popUpModal').modal('hide');
+		}
+	);
+
+	setTimeout(scrollUp, 25); 
+	window.onhashchange = scrollUp;
+
+	getMailUnreadCount();
+	setInterval(getMailUnreadCount, updateInterval);
+
+	getTelegram();
+	setInterval(getTelegram, updateInterval);
+
+}
\ No newline at end of file
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
new file mode 100644
index 0000000000..a176df190c
--- /dev/null
+++ b/web/root/js/forum.js
@@ -0,0 +1,226 @@
+// Add a parameter to the query string
+var insertParam = function(key, value) {
+    key = encodeURIComponent(key);
+    value = encodeURIComponent(value);
+    var kvp = window.location.search.substr(1).split('&');
+    var i = kvp.length,	x;
+    while(i--) {
+        x = kvp[i].split('=');
+        if (x[0]==key) {
+            x[1] = value;
+            kvp[i] = x.join('=');
+            break;
+        }
+    }
+    if(i<0)
+    	kvp[kvp.length] = [key,value].join('=');
+    window.location.search = kvp.join('&'); 
+}
+
+// For now we'll just remove nested quotes from the parent post
+var quotify = function(id) {
+	$('#quote-' + id).attr("disabled", true);
+	var html = $('#message-' + id).clone();
+	html.find('blockquote').remove();
+	$('#replytext-' + id).val(
+		html.text().replace(/\n\s*\n\s*\n/g, '\n\n').split(/\r?\n/).map(
+			function(line) { return ("> " + line); }
+		).join("\n") +
+		$('#replytext-' +id).val()
+	);
+}
+
+// (Try to) post a new message to 'sub' via the web API
+var postNew = function(sub) {
+	$('#newmessage-button').attr("disabled", true);
+	var to = $('#newmessage-to').val();
+	var subject = $('#newmessage-subject').val();
+	var body = $('#newmessage-body').val();
+	$.ajax(
+		{	'url' : "./api/forum.ssjs",
+			'method' : "POST",
+			'data' : {
+				'call' : "post",
+				'sub' : sub,
+				'to' : to,
+				'subject' : subject,
+				'body' : body
+			}
+		}
+	).done(
+		function(data) {
+			if(data.success) {
+				$('#newmessage').remove();
+				insertParam("notice", "Your message has been posted.");
+			}
+			$('#newmessage-button').attr("disabled", false);
+		}
+	);
+}
+
+// (Try to) post a reply to message number 'id' of 'sub' via the web API
+var postReply = function(sub, id) {
+	$('#reply-button-' + id).attr("disabled", true);
+	var body = $('#replytext-' + id).val();
+	$.ajax(
+		{	'url' : "./api/forum.ssjs",
+			'method' : "POST",
+			'data' : {
+				'call' : "post-reply",
+				'sub' : sub,
+				'body' : $('#replytext-' + id).val(),
+				'pid' : id
+			}
+		}
+	).done(
+		function(data) {
+			if(data.success) {
+				$('#quote-' + id).attr("disabled", false);
+				$('#replybox-' + id).remove();
+				insertParam("notice", "Your message has been posted.");
+			} else {
+				$('#reply-button-' + id).attr("disabled", false);
+			}
+		}
+	);
+}
+
+// (Try to) delete a message via the web API
+var deleteMessage = function(sub, id) {
+	$.getJSON(
+		'./api/forum.ssjs?call=delete-message&sub=' + sub + '&number=' + id,
+		function(data) {
+			if(data.success) {
+				$('#li-' + id).remove();
+				insertParam("notice", "Message deleted.");
+			}
+		}
+	);
+}
+
+// Add a new message input form to the element with id 'forum-list-container' for sub 'sub'
+var addNew = function(sub) {
+	if($('#newmessage').length > 0)
+		return;
+	$('#forum-list-container').append(
+		'<li id="newmessage" class="list-group-item">' +
+		'<input id="newmessage-to" class="form-control" type="text" placeholder="To"><br>' +
+		'<input id="newmessage-subject" class="form-control" type="text" placeholder="Subject"><br>' +
+		'<textarea id="newmessage-body" class="form-control" rows="8"></textarea><br>' +
+		'<input id="newmessage-button" class="btn btn-primary" type="submit" value="Submit" onclick="postNew(\'' + sub + '\')">' +
+		'</li>'
+	);
+	$.getJSON(
+		'./api/forum.ssjs?call=get-signature',
+		function(data) {
+			$('#newmessage-body').val($('#newmessage-body').val() + "\r\n" + data.signature);
+		}
+	);
+	window.location.hash = "#newmessage";
+}
+
+// Add a reply input form to the page for message with number 'id' in sub 'sub'
+var addReply = function(sub, id) {
+	if($('#replybox-' + id).length > 0)
+		return;
+	$('#li-' + id).append(
+		'<div class="reply" id="replybox-' + id + '">' +
+		'<strong>Reply</strong>' +
+		'<textarea rows="8" class="form-control reply" id="replytext-' + id + '""></textarea>' +
+		'<button id="quote-' + id + '" class="btn" onclick="quotify(' + id + ')">Quote</button> ' +
+		'<input id="reply-button-' + id + '" class="btn btn-primary" type="submit" value="Submit" onclick="postReply(\'' + sub + '\', ' + id + ')">' +
+		'</div>'
+	);
+	$.getJSON(
+		'./api/forum.ssjs?call=get-signature',
+		function(data) {
+			$('#replytext-' + id).val($('#replytext-' + id).val() + "\r\n" + data.signature);
+		}
+	);
+}
+
+// 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
+var getSubUnreadCount = function(sub) {
+	$.getJSON(
+		"./api/forum.ssjs?call=get-sub-unread-count&sub=" + sub,
+		function(data) {
+			for(sub in data) {
+				if(data[sub].scanned > 0)
+					$('#badge-' + sub).text(data[sub].total);
+				else if(data[sub].total > 0)
+					$('#badge-' + sub).text(data[sub].total);
+				else
+					$('#badge-' + sub).text("");
+			}
+		}
+	);
+}
+
+// 'group' can be a single group index, or a string of 0&group=1&group=2...
+var getGroupUnreadCount = function(group) {
+	$.getJSON(
+		"./api/forum.ssjs?call=get-group-unread-count&group=" + group,
+		function(data) {
+			for(group in data) {
+				$('#badge-scanned-' + group).text((data[group].scanned == 0 ? "" : data[group].scanned));
+				$('#badge-ignored-' + group).text((data[group].total == 0 || data[group].total == data[group].scanned ? "" : (data[group].total - data[group].scanned)));
+			}
+		}
+	);
+}
+
+/*  Fetch a private mail message's body (with links to attachments) where 'id'
+	is the message number.	Output it to an element with id 'message-<id>'. */
+var getMailBody = function(id) {
+	if(!$('#message-' + id).attr('hidden')) {
+		$('#message-' + id).attr('hidden', true);
+	} else if($('#message-' + id).html() != "") {
+		$('#message-' + id).attr('hidden', false);
+	} else {
+		$.getJSON(
+			"./api/forum.ssjs?call=get-mail-body&number=" + id,
+			function(data) {
+				var str = data.body;
+				if(data.inlines.length > 0)
+					str += "<br>Inline attachments: " + data.inlines.join("<br>") + "<br>";
+				if(data.attachments.length > 0)
+					str += "<br>Attachments: " + data.attachments.join("<br>") + "<br>";
+				str +=
+					'<button class="btn btn-default icon" ' +
+					'aria-label="Reply to this message" ' +
+					'title="Reply to this message" ' +
+					'name="reply-' + id + '" ' +
+					'onclick="addReply(\'mail\'),' + id + '">' +
+					'<span class="glyphicon glyphicon-comment"></span>' +
+					'</button>' +
+					'<button class="btn btn-default icon" aria-label="Delete this message" ' +
+					'title="Delete this message" onclick="deleteMessage(\'mail\',' + id + ')">' +
+					'<span class="glyphicon glyphicon-trash"></span>' +
+					'</button>';
+				$('#message-' + id).html(str);
+				$('#message-' + id).attr('hidden', false);
+			}
+		);
+	}
+}
+
+var setScanCfg = function(sub, cfg) {
+	var opts = [
+		"scan-cfg-off",
+		"scan-cfg-new",
+		"scan-cfg-youonly"
+	];
+	$.getJSON(
+		"./api/forum.ssjs?call=set-scan-cfg&sub=" + sub + "&cfg=" + cfg,
+		function(data) {
+			if(!data.success)
+				return;
+			opts.forEach(
+				function(opt, index) {
+					$('#' + opt).toggleClass('btn-primary', (cfg == index));
+					$('#' + opt).toggleClass('btn-default', (cfg != index));
+				}
+			);
+		}
+	);
+}
\ No newline at end of file
diff --git a/web/root/js/jquery.min.js b/web/root/js/jquery.min.js
new file mode 100644
index 0000000000..f3644431ee
--- /dev/null
+++ b/web/root/js/jquery.min.js
@@ -0,0 +1,6 @@
+/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function qa(){}qa.prototype=d.filters=d.pseudos,d.setFilters=new qa,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function ra(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;
+
+return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==ca()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===ca()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?aa:ba):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:ba,isPropagationStopped:ba,isImmediatePropagationStopped:ba,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=aa,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=aa,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=aa,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=ba;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=ba),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function da(a){var b=ea.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var ea="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fa=/ jQuery\d+="(?:null|\d+)"/g,ga=new RegExp("<(?:"+ea+")[\\s/>]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/<tbody/i,la=/<|&#?\w+;/,ma=/<(?:script|style|link)/i,na=/checked\s*(?:[^=]|=\s*.checked.)/i,oa=/^$|\/(?:java|ecma)script/i,pa=/^true\/(.*)/,qa=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ra={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?"<table>"!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Ca[0].contentWindow||Ca[0].contentDocument).document,b.write(),b.close(),c=Ea(a,b),Ca.detach()),Da[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Ga=/^margin/,Ha=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ia,Ja,Ka=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ia=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)},Ja=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ia(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Ha.test(g)&&Ga.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ia=function(a){return a.currentStyle},Ja=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ia(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Ha.test(g)&&!Ka.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function La(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight),b.removeChild(i)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Ma=/alpha\([^)]*\)/i,Na=/opacity\s*=\s*([^)]*)/,Oa=/^(none|table(?!-c[ea]).+)/,Pa=new RegExp("^("+S+")(.*)$","i"),Qa=new RegExp("^([+-])=("+S+")","i"),Ra={position:"absolute",visibility:"hidden",display:"block"},Sa={letterSpacing:"0",fontWeight:"400"},Ta=["Webkit","O","Moz","ms"];function Ua(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Ta.length;while(e--)if(b=Ta[e]+c,b in a)return b;return d}function Va(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fa(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wa(a,b,c){var d=Pa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xa(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Ya(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ia(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Ja(a,b,f),(0>e||null==e)&&(e=a.style[b]),Ha.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xa(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Ja(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ua(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qa.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ua(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Ja(a,b,d)),"normal"===f&&b in Sa&&(f=Sa[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Oa.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Ra,function(){return Ya(a,b,d)}):Ya(a,b,d):void 0},set:function(a,c,d){var e=d&&Ia(a);return Wa(a,c,d?Xa(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Na.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Ma,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Ma.test(f)?f.replace(Ma,e):f+" "+e)}}),m.cssHooks.marginRight=La(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Ja,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Ga.test(a)||(m.cssHooks[a+b].set=Wa)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ia(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Va(this,!0)},hide:function(){return Va(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Za(a,b,c,d,e){
+return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[c]=eb[c]||[],eb[c].unshift(b)},prefilter:function(a,b){b?db.unshift(a):db.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kb(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),m.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($a=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$a=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_a||(_a=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_a),_a=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lb=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lb,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mb,nb,ob=m.expr.attrHandle,pb=/^(?:checked|selected)$/i,qb=k.getSetAttribute,rb=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nb:mb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rb&&qb||!pb.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qb?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nb={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rb&&qb||!pb.test(c)?a.setAttribute(!qb&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ob[b]||m.find.attr;ob[b]=rb&&qb||!pb.test(b)?function(a,b,d){var e,f;return d||(f=ob[b],ob[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,ob[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rb&&qb||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mb&&mb.set(a,b,c)}}),qb||(mb={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},ob.id=ob.name=ob.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mb.set},m.attrHooks.contenteditable={set:function(a,b,c){mb.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sb=/^(?:input|select|textarea|button|object)$/i,tb=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sb.test(a.nodeName)||tb.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var ub=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ub," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ub," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ub," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vb=m.now(),wb=/\?/,xb=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xb,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yb,zb,Ab=/#.*$/,Bb=/([?&])_=[^&]*/,Cb=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Db=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Eb=/^(?:GET|HEAD)$/,Fb=/^\/\//,Gb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hb={},Ib={},Jb="*/".concat("*");try{zb=location.href}catch(Kb){zb=y.createElement("a"),zb.href="",zb=zb.href}yb=Gb.exec(zb.toLowerCase())||[];function Lb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mb(a,b,c,d){var e={},f=a===Ib;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nb(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Ob(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zb,type:"GET",isLocal:Db.test(yb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nb(Nb(a,m.ajaxSettings),b):Nb(m.ajaxSettings,a)},ajaxPrefilter:Lb(Hb),ajaxTransport:Lb(Ib),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cb.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zb)+"").replace(Ab,"").replace(Fb,yb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gb.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yb[1]&&c[2]===yb[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yb[3]||("http:"===yb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mb(Hb,k,b,v),2===t)return v;h=m.event&&k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Eb.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wb.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bb.test(e)?e.replace(Bb,"$1_="+vb++):e+(wb.test(e)?"&":"?")+"_="+vb++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jb+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mb(Ib,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Ob(k,v,c)),u=Pb(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qb=/%20/g,Rb=/\[\]$/,Sb=/\r?\n/g,Tb=/^(?:submit|button|image|reset|file)$/i,Ub=/^(?:input|select|textarea|keygen)/i;function Vb(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rb.test(a)?d(a,e):Vb(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vb(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vb(c,a[c],b,e);return d.join("&").replace(Qb,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Ub.test(this.nodeName)&&!Tb.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sb,"\r\n")}}):{name:b.name,value:c.replace(Sb,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zb()||$b()}:Zb;var Wb=0,Xb={},Yb=m.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Xb)Xb[a](void 0,!0)}),k.cors=!!Yb&&"withCredentials"in Yb,Yb=k.ajax=!!Yb,Yb&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xb[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xb[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zb(){try{return new a.XMLHttpRequest}catch(b){}}function $b(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _b=[],ac=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_b.pop()||m.expando+"_"+vb++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ac.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ac.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ac,"$1"+e):b.jsonp!==!1&&(b.url+=(wb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_b.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bc=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bc)return bc.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cc=a.document.documentElement;function dc(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dc(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cc;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cc})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dc(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=La(k.pixelPosition,function(a,c){return c?(c=Ja(a,b),Ha.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ec=a.jQuery,fc=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fc),b&&a.jQuery===m&&(a.jQuery=ec),m},typeof b===K&&(a.jQuery=a.$=m),m});
+//# sourceMappingURL=jquery.min.map
\ No newline at end of file
diff --git a/web/root/js/offcanvas.js b/web/root/js/offcanvas.js
new file mode 100644
index 0000000000..d2f37f22c9
--- /dev/null
+++ b/web/root/js/offcanvas.js
@@ -0,0 +1,5 @@
+$(document).ready(function () {
+  $('[data-toggle="offcanvas"]').click(function () {
+    $('.row-offcanvas').toggleClass('active')
+  });
+});
\ No newline at end of file
diff --git a/web/root/js/validator.js b/web/root/js/validator.js
new file mode 100644
index 0000000000..687d9839fa
--- /dev/null
+++ b/web/root/js/validator.js
@@ -0,0 +1,325 @@
+/* ========================================================================
+ * Bootstrap (plugin): validator.js v0.9.0
+ * ========================================================================
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Cina Saffary.
+ * Made by @1000hz in the style of Bootstrap 3 era @fat
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // VALIDATOR CLASS DEFINITION
+  // ==========================
+
+  var Validator = function (element, options) {
+    this.$element = $(element)
+    this.options  = options
+
+    options.errors = $.extend({}, Validator.DEFAULTS.errors, options.errors)
+
+    for (var custom in options.custom) {
+      if (!options.errors[custom]) throw new Error('Missing default error message for custom validator: ' + custom)
+    }
+
+    $.extend(Validator.VALIDATORS, options.custom)
+
+    this.$element.attr('novalidate', true) // disable automatic native validation
+    this.toggleSubmit()
+
+    this.$element.on('input.bs.validator change.bs.validator focusout.bs.validator', $.proxy(this.validateInput, this))
+    this.$element.on('submit.bs.validator', $.proxy(this.onSubmit, this))
+
+    this.$element.find('[data-match]').each(function () {
+      var $this  = $(this)
+      var target = $this.data('match')
+
+      $(target).on('input.bs.validator', function (e) {
+        $this.val() && $this.trigger('input.bs.validator')
+      })
+    })
+  }
+
+  Validator.INPUT_SELECTOR = ':input:not([type="submit"], button):enabled:visible'
+
+  Validator.DEFAULTS = {
+    delay: 500,
+    html: false,
+    disable: true,
+    custom: {},
+    errors: {
+      match: 'Does not match',
+      minlength: 'Not long enough'
+    },
+    feedback: {
+      success: 'glyphicon-ok',
+      error: 'glyphicon-remove'
+    }
+  }
+
+  Validator.VALIDATORS = {
+    'native': function ($el) {
+      var el = $el[0]
+      return el.checkValidity ? el.checkValidity() : true
+    },
+    'match': function ($el) {
+      var target = $el.data('match')
+      return !$el.val() || $el.val() === $(target).val()
+    },
+    'minlength': function ($el) {
+      var minlength = $el.data('minlength')
+      return !$el.val() || $el.val().length >= minlength
+    }
+  }
+
+  Validator.prototype.validateInput = function (e) {
+    var $el        = $(e.target)
+    var prevErrors = $el.data('bs.validator.errors')
+    var errors
+
+    if ($el.is('[type="radio"]')) $el = this.$element.find('input[name="' + $el.attr('name') + '"]')
+
+    this.$element.trigger(e = $.Event('validate.bs.validator', {relatedTarget: $el[0]}))
+
+    if (e.isDefaultPrevented()) return
+
+    var self = this
+
+    this.runValidators($el).done(function (errors) {
+      $el.data('bs.validator.errors', errors)
+
+      errors.length ? self.showErrors($el) : self.clearErrors($el)
+
+      if (!prevErrors || errors.toString() !== prevErrors.toString()) {
+        e = errors.length
+          ? $.Event('invalid.bs.validator', {relatedTarget: $el[0], detail: errors})
+          : $.Event('valid.bs.validator', {relatedTarget: $el[0], detail: prevErrors})
+
+        self.$element.trigger(e)
+      }
+
+      self.toggleSubmit()
+
+      self.$element.trigger($.Event('validated.bs.validator', {relatedTarget: $el[0]}))
+    })
+  }
+
+
+  Validator.prototype.runValidators = function ($el) {
+    var errors   = []
+    var deferred = $.Deferred()
+    var options  = this.options
+
+    $el.data('bs.validator.deferred') && $el.data('bs.validator.deferred').reject()
+    $el.data('bs.validator.deferred', deferred)
+
+    function getErrorMessage(key) {
+      return $el.data(key + '-error')
+        || $el.data('error')
+        || key == 'native' && $el[0].validationMessage
+        || options.errors[key]
+    }
+
+    $.each(Validator.VALIDATORS, $.proxy(function (key, validator) {
+      if (($el.data(key) || key == 'native') && !validator.call(this, $el)) {
+        var error = getErrorMessage(key)
+        !~errors.indexOf(error) && errors.push(error)
+      }
+    }, this))
+
+    if (!errors.length && $el.val() && $el.data('remote')) {
+      this.defer($el, function () {
+        var data = {}
+        data[$el.attr('name')] = $el.val()
+        $.get($el.data('remote'), data)
+          .fail(function (jqXHR, textStatus, error) { errors.push(getErrorMessage('remote') || error) })
+          .always(function () { deferred.resolve(errors)})
+      })
+    } else deferred.resolve(errors)
+
+    return deferred.promise()
+  }
+
+  Validator.prototype.validate = function () {
+    var delay = this.options.delay
+
+    this.options.delay = 0
+    this.$element.find(Validator.INPUT_SELECTOR).trigger('input.bs.validator')
+    this.options.delay = delay
+
+    return this
+  }
+
+  Validator.prototype.showErrors = function ($el) {
+    var method = this.options.html ? 'html' : 'text'
+
+    this.defer($el, function () {
+      var $group = $el.closest('.form-group')
+      var $block = $group.find('.help-block.with-errors')
+      var $feedback = $group.find('.form-control-feedback')
+      var errors = $el.data('bs.validator.errors')
+
+      if (!errors.length) return
+
+      errors = $('<ul/>')
+        .addClass('list-unstyled')
+        .append($.map(errors, function (error) { return $('<li/>')[method](error) }))
+
+      $block.data('bs.validator.originalContent') === undefined && $block.data('bs.validator.originalContent', $block.html())
+      $block.empty().append(errors)
+      $group.addClass('has-error')
+
+      $feedback.length
+        && $feedback.removeClass(this.options.feedback.success)
+        && $feedback.addClass(this.options.feedback.error)
+        && $group.removeClass('has-success')
+    })
+  }
+
+  Validator.prototype.clearErrors = function ($el) {
+    var $group = $el.closest('.form-group')
+    var $block = $group.find('.help-block.with-errors')
+    var $feedback = $group.find('.form-control-feedback')
+
+    $block.html($block.data('bs.validator.originalContent'))
+    $group.removeClass('has-error')
+
+    $feedback.length
+      && $feedback.removeClass(this.options.feedback.error)
+      && $feedback.addClass(this.options.feedback.success)
+      && $group.addClass('has-success')
+  }
+
+  Validator.prototype.hasErrors = function () {
+    function fieldErrors() {
+      return !!($(this).data('bs.validator.errors') || []).length
+    }
+
+    return !!this.$element.find(Validator.INPUT_SELECTOR).filter(fieldErrors).length
+  }
+
+  Validator.prototype.isIncomplete = function () {
+    function fieldIncomplete() {
+      return this.type === 'checkbox' ? !this.checked                                   :
+             this.type === 'radio'    ? !$('[name="' + this.name + '"]:checked').length :
+                                        $.trim(this.value) === ''
+    }
+
+    return !!this.$element.find(Validator.INPUT_SELECTOR).filter('[required]').filter(fieldIncomplete).length
+  }
+
+  Validator.prototype.onSubmit = function (e) {
+    this.validate()
+    if (this.isIncomplete() || this.hasErrors()) e.preventDefault()
+  }
+
+  Validator.prototype.toggleSubmit = function () {
+    if(!this.options.disable) return
+
+    var $btn = $('button[type="submit"], input[type="submit"]')
+      .filter('[form="' + this.$element.attr('id') + '"]')
+      .add(this.$element.find('input[type="submit"], button[type="submit"]'))
+
+    $btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
+  }
+
+  Validator.prototype.defer = function ($el, callback) {
+    callback = $.proxy(callback, this)
+    if (!this.options.delay) return callback()
+    window.clearTimeout($el.data('bs.validator.timeout'))
+    $el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay))
+  }
+
+  Validator.prototype.destroy = function () {
+    this.$element
+      .removeAttr('novalidate')
+      .removeData('bs.validator')
+      .off('.bs.validator')
+
+    this.$element.find(Validator.INPUT_SELECTOR)
+      .off('.bs.validator')
+      .removeData(['bs.validator.errors', 'bs.validator.deferred'])
+      .each(function () {
+        var $this = $(this)
+        var timeout = $this.data('bs.validator.timeout')
+        window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout')
+      })
+
+    this.$element.find('.help-block.with-errors').each(function () {
+      var $this = $(this)
+      var originalContent = $this.data('bs.validator.originalContent')
+
+      $this
+        .removeData('bs.validator.originalContent')
+        .html(originalContent)
+    })
+
+    this.$element.find('input[type="submit"], button[type="submit"]').removeClass('disabled')
+
+    this.$element.find('.has-error').removeClass('has-error')
+
+    return this
+  }
+
+  // VALIDATOR PLUGIN DEFINITION
+  // ===========================
+
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option)
+      var data    = $this.data('bs.validator')
+
+      if (!data && option == 'destroy') return
+      if (!data) $this.data('bs.validator', (data = new Validator(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.validator
+
+  $.fn.validator             = Plugin
+  $.fn.validator.Constructor = Validator
+
+
+  // VALIDATOR NO CONFLICT
+  // =====================
+
+  $.fn.validator.noConflict = function () {
+    $.fn.validator = old
+    return this
+  }
+
+
+  // VALIDATOR DATA-API
+  // ==================
+
+  $(window).on('load', function () {
+    $('form[data-toggle="validator"]').each(function () {
+      var $form = $(this)
+      Plugin.call($form, $form.data())
+    })
+  })
+
+}(jQuery);
diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
new file mode 100644
index 0000000000..8480175022
--- /dev/null
+++ b/web/root/pages/000-home.xjs
@@ -0,0 +1,24 @@
+<!--Home-->
+<?xjs 
+	if(typeof argv[0] != "boolean" || !argv[0])
+		exit();
+	load(settings.web_lib + "ftelnet.js");
+?>
+
+<script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
+<div id="fTelnetContainer" style="margin-bottom:1em;"></div>
+<div class="row">
+	<div class="center-block" style="width:200px;margin-bottom:1em;">
+		<button id="ftelnet-connect" class="btn btn-primary">Connect via Telnet</button>
+	</div>
+</div>
+<script type="text/javascript">
+	fTelnet.Hostname = "<?xjs write(system.inet_addr); ?>";
+	fTelnet.Port = <?xjs write((webSocket === null) ? 1123 : webSocket.Port); ?>;
+	fTelnet.ConnectionType = "telnet";
+	fTelnet.SplashScreen = "<?xjs write(getSplash()); ?>";
+	fTelnet.StatusBarVisible = false;
+	fTelnet.VirtualKeyboardVisible = (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
+	fTelnet.Init();
+	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+</script>
diff --git a/web/root/pages/000-mail.ssjs b/web/root/pages/000-mail.ssjs
new file mode 100644
index 0000000000..40eabd8f2f
--- /dev/null
+++ b/web/root/pages/000-mail.ssjs
@@ -0,0 +1,89 @@
+//HIDDEN
+
+if(typeof argv[0] != "boolean" || !argv[0])
+	exit();
+
+if(user.number == 0 || user.alias == settings.guest)
+	exit();
+
+load("sbbsdefs.js");
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "forum.js");
+
+writeln('<script type="text/javascript" src="./js/forum.js"></script>');
+
+if(typeof http_request.query.notice != "undefined") {
+	writeln(
+		'<div id="noticebox" class="alert alert-warning">' + 
+		http_request.query.notice[0] + '</div>' +
+		'<script type="text/javascript">' +
+		'$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});' +
+		'</script>'
+	);
+}
+
+if(	user.alias != settings.guest
+	&&
+	!(user.security.restrictions&UFLAG_E)
+	&&
+	!(user.security.restrictions&UFLAG_M)
+) {
+	writeln(
+		'<button class="btn btn-default icon" ' +
+		'aria-label="Post a new message" title="Post a new message" ' +
+		'onclick="addNew(\'mail\')">' +
+		'<span class="glyphicon glyphicon-pencil"></span>' +
+		'</button>'
+	);
+}
+
+
+writeln(
+	format(
+		'<ul class="nav nav-tabs">' +
+		'<li role="presentation" class="%s">' +
+		'<a href="./?page=%s&amp;sent=0">Inbox</a>' +
+		'</li>' +
+		'<li role="presentation" class="%s">' +
+		'<a href="./?page=%s&amp;sent=1">Sent</a>' +
+		'</li>' +
+		'</ul><br>',
+		(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? "active" : ""),
+		http_request.query.page[0],
+		(typeof http_request.query.sent != "undefined" && http_request.query.sent[0] == "1" ? "active" : ""),
+		http_request.query.page[0]
+	)
+);
+
+writeln('<ul id="forum-list-container" class="list-group">');
+
+var writeMessage = function(header) {
+	writeln(
+		format(
+			'<li id="li-' + header.number + '" class="list-group-item mail striped %s">',
+			(header.attr&MSG_READ ? "read" : "unread"),
+			header.number
+		)
+	);
+	writeln(
+		format(
+			'<div style="cursor:pointer;" onclick="getMailBody(%s)">' +
+			'%s: <strong>%s</strong> on %s' +
+			'<p>Subject: <strong>%s</strong></p></div>' +
+			'<div class="message" id="message-%s" hidden></div>',
+			header.number,
+			(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? "From" : "To"),
+			(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? header.from : header.to),
+			(new Date(header.when_written_time * 1000)).toLocaleString(),
+			header.subject,
+			header.number
+		)
+	);
+	writeln('</li>');
+}
+
+getMailHeaders(
+	(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? false : true)
+).forEach(writeMessage);
+
+writeln('</ul>');
\ No newline at end of file
diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
new file mode 100644
index 0000000000..7f8c2496df
--- /dev/null
+++ b/web/root/pages/000-register.xjs
@@ -0,0 +1,192 @@
+<!--HIDDEN-->
+
+<!--
+	Username/alias and password are always required. All other fields are set
+	as 'required' (or not) based on your New User Question Toggles as set in
+	the System Configuration section of scfg.  You can delete any fields of
+	this form that your system does not require.
+-->
+
+<?xjs
+	if(user.number > 0 && user.alias != settings.guest)
+		exit();
+
+	load("sbbsdefs.js");
+
+	var required = function(mask) {
+		return ((system.new_user_questions&mask) ? " required" : "");
+	}
+
+	var notRequired = function(mask) {
+		return ((system.new_user_questions&mask) ? "" : " required");
+	}
+
+	var iconRequired = function(mask) {
+		if(required(mask).length > 0)
+			write('<span title="Required" class="glyphicon glyphicon-asterisk"></span>');
+	}
+
+	var iconNotRequired = function(mask) {
+		if(notRequired(mask).length > 0)
+			write('<span title="Required" class="glyphicon glyphicon-asterisk"></span>');
+	}
+
+?>
+
+<script type="text/javascript" src="./js/validator.js"></script>
+
+<div class="well well-sm"><h3>Register</h3></div>
+
+<div id="errorbox" class="bg-danger" hidden></div>
+
+<div id="form-register-container">
+
+	<div style="margin-bottom:1em;">Fields marked with <span title="Required" class="glyphicon glyphicon-asterisk"></span> are required.  All others can be left blank if you wish.</div>
+
+	<form id="form-register" data-toggle="validator">
+
+		<div class="form-group">
+			<label for="alias">Username</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_ALIAS); ?>" class="form-control" id="alias" name="alias" placeholder="Username" required>
+			<span class="help-block">Maximum of <?xjs write(LEN_ALIAS); ?> characters</span>
+		</div>
+
+		<div class="form-group">
+			<label for="password1" class="control-label">Password</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
+			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="Password" required>
+			<span class="help-block">Minimum of <?xjs write(settings.minimum_password_length); ?>, maximum of <?xjs write(LEN_PASS); ?> characters</span>
+		</div>
+			<div class="form-group">
+			<label for="password2" class="control-label">Confirm Password</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
+			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="Confirm Password" data-match="#password1" required>
+		</div>
+
+		<div class="form-group">
+			<label for="netmail">Email address</label> <?xjs iconNotRequired(UQ_NONETMAIL); ?>
+			<input type="email" data-minlength="6" maxlength="<?xjs write(LEN_NETMAIL); ?>"class="form-control" id="netmail" name="netmail" placeholder="pat@m.f"<?xjs write(notRequired(UQ_NONETMAIL)); ?>>
+			<span class="help-block">Maximum of <?xjs write(LEN_NETMAIL); ?> characters</span>
+		</div>
+
+		<div class="form-group">
+			<label for="realname">Real Name</label> <?xjs iconRequired(UQ_REALNAME); ?>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_NAME); ?>" class="form-control" id="realname" name="realname" placeholder="Pat Androgyne"<?xjs write(required(UQ_REALNAME)); ?>>
+			<span class="help-block">Maximum of <?xjs write(LEN_NAME); ?> characters</span>
+		</div>
+
+<!--	Not sure which User property this would be
+		<div class="form-group">
+			<label for="company">Organization</label> <?xjs //iconRequired(UQ_COMPANY); ?>
+			<input type="text" class="form-control" name="company" placeholder="Company name"<?xjs //write(required(UQ_COMPANY)); ?>>
+		</div> -->
+
+		<div class="form-group">
+			<label for="address">Street address</label> <?xjs iconRequired(UQ_ADDRESS); ?>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="address" name="address" placeholder="123 Any Street"<?xjs write(required(UQ_ADDRESS)); ?>>
+			<span class="help-block">Maximum of <?xjs write(LEN_ADDRESS); ?> characters</span>
+		</div>
+
+		<div class="form-group">
+			<label for="location">Location</label> <?xjs iconRequired(UQ_LOCATION); ?>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_LOCATION); ?>" class="form-control" id="location" name="location" placeholder="City, State"<?xjs write(required(UQ_LOCATION)); ?>>
+			<span class="help-block">Maximum of <?xjs write(LEN_LOCATION); ?> characters</span>
+		</div>
+
+		<div class="form-group">
+			<label for="phone">Telephone number</label> <?xjs iconRequired(UQ_PHONE); ?>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_PHONE); ?>" class="form-control" id="phone" name="phone" placeholder="800-555-5555"<?xjs write(required(UQ_PHONE)); ?>>
+			<span class="help-block">Maximum of <?xjs write(LEN_PHONE); ?> characters</span>
+		</div>
+
+		<div class="form-group">
+			<label for="birth">Birthdate</label> <?xjs iconRequired(UQ_BIRTH); ?>
+			<input type="text" data-minlength="<?xjs write(LEN_BIRTH); ?>" maxlength="<?xjs write(LEN_BIRTH); ?>" class="form-control" id="birth" name="birth" placeholder="<?xjs write(system.settings&SYS_EURODATE ? 'DD/MM/YY' : 'MM/DD/YY'); ?>" <?xjs write(required(UQ_BIRTH)); ?>>
+			<span class="help-block">Maximum of <?xjs write(LEN_BIRTH); ?> characters</span>
+		</div>
+
+		<div class="form-inline">
+			<?xjs iconRequired(UQ_SEX); ?> <label>Gender</label>&nbsp;
+			<div class="radio">
+				<label>
+					<input type="radio" id="gender-withheld" name="gender-withheld" checked> Withheld 
+				</label>
+			</div>
+			<div class="radio">
+				<label>
+					<input type="radio" id="gender-male" name="gender-male"> Male 
+				</label>
+			</div>
+			<div class="radio">
+				<label>
+					<input type="radio" id="gender-female" name="gender-female"> Female
+				</label>
+			</div>
+			<div class="radio">
+				<label>
+					<input type="radio" id="gender-other" name="gender-other"> Other 
+				</label>
+			</div>
+		</div>
+		<br>
+
+		<?xjs
+			if(system.newuser_password != "") {
+				writeln('<div class="form-group">');
+				writeln('<label for="newuser-password">Registration password</label>');
+				writeln('<input type="password" id="newuser-password" name="newuser-password" data-minlength="1" maxlength="8">');
+				writeln('</div>');
+			}
+		?>
+
+		<!-- Traps - leave these here. They will be hidden from users. -->
+		<input id="send-me-free-stuff" title="Don't fill this field" name="send-me-free-stuff" type="text">
+		<input id="subscribe-to-newsletter" title="Don't check this box" name="subscribe-to-newsletter" value="1" type="checkbox">
+
+		<div class="pull-right">
+			<button class="btn btn-primary" type="submit">
+				Register
+			</button>
+		</div>
+
+	</form>
+
+</div>
+
+<script type="text/javascript">
+
+var register = function() {
+	$.post(
+	 	"./api/register.ssjs",
+		$('#form-register').serialize()
+	).done(
+	 	function(data) {
+	 		if(data.errors.length > 0) {
+	 			$('#errorbox').html("");
+	 			data.errors.forEach(
+	 				function(err) {
+	 					$('#errorbox').append("<p>" + err + "</p>");
+	 				}
+	 			);
+	 			$('#errorbox').attr("hidden", false);
+	 		} else {
+	 			$('#errorbox').attr("hidden", true);
+	 			$('#form-register-container').html("Your account has been created.");
+	 		}
+	 	}
+	);
+}
+
+$('#form-register').validator().on(
+	'submit',
+	function(evt) {
+		if(evt.isDefaultPrevented())
+			return;
+		evt.preventDefault();
+		register();
+	}
+);
+
+// Traps
+$('#send-me-free-stuff').attr('hidden', true);
+$('#subscribe-to-newsletter').attr('hidden', true);
+
+</script>
\ No newline at end of file
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
new file mode 100644
index 0000000000..6e9b18aa1a
--- /dev/null
+++ b/web/root/pages/001-forum.ssjs
@@ -0,0 +1,379 @@
+//Forum
+
+if(typeof argv[0] != "boolean" || !argv[0])
+	exit();
+
+load("sbbsdefs.js");
+load("msgutils.js");
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "forum.js");
+
+writeln('<script type="text/javascript" src="./js/forum.js"></script>');
+
+if(typeof http_request.query.notice != "undefined") {
+	writeln(
+		'<div id="noticebox" class="alert alert-warning">' + 
+		http_request.query.notice[0] + '</div>' +
+		'<script type="text/javascript">' +
+		'$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});' +
+		'</script>'
+	);
+}
+
+
+if(	typeof http_request.query.sub != "undefined"
+	&&
+	typeof msg_area.sub[http_request.query.sub[0]] != "undefined"
+	&&
+	typeof http_request.query.thread != "undefined"
+) {
+
+	// Thread view
+	var msgBase = new MsgBase(http_request.query.sub[0]);
+
+	var firstUnreadLi = "";
+
+	var writeMessage = function(header, index) {
+
+		var body = msgBase.get_msg_body(header.number);
+		if(body === null)
+			return;
+
+		writeln(
+			'<li class="list-group-item striped' +
+			'" id="li-' + header.number + '">' +
+			'<a id="' + header.number + '"></a>'
+		);
+
+		// Show subject if first message
+		if(index == 0)
+			writeln('<h4><strong>' + header.subject + '</strong></h4>');
+
+		// Header
+		if(user.alias != settings.guest && header.number > msg_area.sub[http_request.query.sub[0]].scan_ptr) {
+			writeln('<span title="Unread" class="glyphicon glyphicon-star"></span>');
+			if(firstUnreadLi == "")
+				firstUnreadLi = "li-" + header.number;
+		}			
+		writeln(
+			'From <strong>' + header.from + "</strong>" +
+			(header.from_net_type == 0 ? " " : "@" +  header.from_net_addr + " ") +
+			'to <strong>' + header.to + '</strong> ' +
+			'on ' +
+			(new Date(header.when_written_time * 1000)).toLocaleString()
+		);
+
+		// Body
+		writeln(
+			'<div class="message" id="message-' + header.number + '">' +
+			formatMessage(body) + '</div>'
+		);
+
+		// Standard controls
+		writeln(
+			'<a class="btn btn-default icon" title="Jump to oldest message" ' +
+			'aria-label="Jump to oldest message" href="#' + header.ec_thread.messages[0].number + '">' +
+			'<span class="glyphicon glyphicon-fast-backward"></span></a>' +
+
+			'<a class="btn btn-default icon" title="Direct link to this message" ' +
+			'aria-label="Direct link to this message" href="#' + header.number + '">' +
+			'<span class="glyphicon glyphicon-link"></span></a>' +
+			
+			'<a class="btn btn-default icon" aria-label="Jump to newest message" title="Jump to newest message" href="#' + 
+			header.ec_thread.messages[header.ec_thread.messages.length - 1].number + 
+			'"><span class="glyphicon glyphicon-fast-forward"></span></a>'
+		);
+
+		// User can post
+		if(user.alias != settings.guest && msg_area.sub[msgBase.cfg.code].can_post) {
+			writeln(
+				'<button class="btn btn-default icon" ' +
+				'aria-label="Reply to this message" ' +
+				'title="Reply to this message" ' +
+				'name="reply-' + header.number + '" ' +
+				'onclick="addReply(\'' + msgBase.cfg.code + '\',' + header.number + ')">' +
+				'<span class="glyphicon glyphicon-comment"></span>' +
+				'</button>'
+			);
+		}
+
+		// User is operator
+		if(user.alias != settings.guest && msg_area.sub[msgBase.cfg.code].is_operator) {
+			writeln(
+				'<button class="btn btn-default icon" aria-label="Delete this message" title="Delete this message" onclick="deleteMessage(\'' + msgBase.cfg.code + '\', ' + header.number + ')" href="#">' +
+				'<span class="glyphicon glyphicon-trash"></span>' +
+				'</button>'
+			);
+		}
+
+		writeln('</li>');
+
+	}
+
+	writeln(
+		format(
+			'<ol class="breadcrumb">' +
+			'<li><a href="./?page=%s">Forum</a></li>' +
+			'<li><a href="./?page=%s&amp;group=%s">%s</a></li>' +
+			'<li><a href="./?page=%s&amp;sub=%s">%s</a></li>' +
+			'</ol>',
+			http_request.query.page[0],
+			http_request.query.page[0],
+			msg_area.sub[http_request.query.sub[0]].grp_index,
+			msg_area.sub[http_request.query.sub[0]].grp_name,
+			http_request.query.page[0],
+			http_request.query.sub[0],
+			msg_area.sub[http_request.query.sub[0]].name
+		)
+	);
+	writeln(
+		'<div id="jump-unread-container" style="margin-bottom:1em;" hidden>' +
+		'<a class="btn btn-default" id="jump-unread" title="Jump to first unread message" href="#">' +
+		'<span class="glyphicon glyphicon-star"></span>' +
+		'</a></div>'
+	);
+
+	try {
+		msgBase.open();
+		var messages = getMessageThreads(
+			http_request.query.sub[0]
+		).thread[
+			http_request.query.thread[0]
+		].messages;
+		writeln('<ul id="forum-list-container" class="list-group">');
+		messages.forEach(writeMessage);
+		writeln('</ul>');
+		msgBase.close();
+		if(messages.length > 1 && firstUnreadLi != "") {
+			writeln(
+				'<script type="text/javascript">' +
+				'$("#jump-unread").attr("href", "#' + firstUnreadLi + '");' +
+				'$("#jump-unread-container").attr("hidden", false);' +
+				'</script>'
+			);
+		}
+		// Update scan pointer
+		if(messages[messages.length - 1].number > msg_area.sub[http_request.query.sub[0]].scan_ptr)
+			msg_area.sub[http_request.query.sub[0]].scan_ptr = messages[messages.length - 1].number;
+	} catch(err) {
+		log(err);
+	}
+
+} else if(		
+	typeof http_request.query.sub != "undefined"
+	&&
+	typeof msg_area.sub[http_request.query.sub[0]] != "undefined"
+) {
+
+	// Thread list
+	var writeThread = function(thread) {
+		if(user.number > 0 && user.alias != settings.guest)
+			var unread = getUnreadInThread(http_request.query.sub[0], thread);
+		else
+			var unread = 0;
+		writeln(
+			format(
+				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" class="list-group-item striped">' +
+				'<strong>%s</strong>' +
+				'<p>By <strong>%s</strong> on %s</p>' +
+				'<span title="Unread messages" class="badge%s" id="badge-%s">%s</span>' +
+				'<p>Latest reply by <strong>%s</strong> on %s</p>' +
+				'</a>',
+				http_request.query.page[0],
+				http_request.query.sub[0],
+				(thread.messages[0].thread_id == 0 ? thread.messages[0].number : thread.messages[0].thread_id),
+				thread.messages[0].subject,
+				thread.messages[0].from,
+				(new Date(thread.messages[0].when_written_time * 1000)).toLocaleString(),
+				((msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) ? " scanned" : ""),
+				thread.messages[0].number,
+				(unread == 0 ? "" : unread),
+				thread.messages[thread.messages.length - 1].from,
+				(new Date(
+					thread.messages[
+						thread.messages.length - 1
+					].when_written_time * 1000
+				)).toLocaleString()
+			)
+		);
+	}
+
+	writeln(
+		format(
+			'<ol class="breadcrumb">' +
+			'<li><a href="./?page=%s">Forum</a></li>' +
+			'<li><a href="./?page=%s&amp;group=%s">%s</a></li>' +
+			'<li><a href="./?page=%s&amp;sub=%s">%s</a></li>' +
+			'</ol>',
+			http_request.query.page[0],
+			http_request.query.page[0],
+			msg_area.sub[http_request.query.sub[0]].grp_index,
+			msg_area.sub[http_request.query.sub[0]].grp_name,
+			http_request.query.page[0],
+			http_request.query.sub[0],
+			msg_area.sub[http_request.query.sub[0]].name
+		)
+	);
+
+	if(user.alias != settings.guest && msg_area.sub[http_request.query.sub[0]].can_post) {
+		writeln(
+			'<button class="btn btn-default icon" ' +
+			'aria-label="Post a new message" title="Post a new message" ' +
+			'onclick="addNew(\'' + http_request.query.sub[0] + '\')">' +
+			'<span class="glyphicon glyphicon-pencil"></span>' +
+			'</button>'
+		);
+	}
+
+	if(user.alias != settings.guest) {
+		writeln(
+			'<button id="scan-cfg-new" class="btn ' +
+			(!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) && (msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) ? "btn-primary" : "btn-default") +
+			' icon" aria-label="Scan for new messages" title="Scan for new messages" ' +
+			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',1)"' +
+			'>' +
+			'<span class="glyphicon glyphicon-ok-sign"></span>' +
+			'</button>'
+		);
+		writeln(
+			'<button id="scan-cfg-youonly" class="btn ' +
+			((msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) ? "btn-primary" : "btn-default") +
+			' icon" aria-label="Scan for new messages to you only" title="Scan for new messages to you only" ' +
+			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',2)"' +
+			'>' +
+			'<span class="glyphicon glyphicon-user"></span>' +
+			'</button>'
+		);
+		writeln(
+			'<button id="scan-cfg-off" class="btn ' +
+			(!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) && !(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) ? "btn-primary" : "btn-default") +
+			' icon" aria-label="Do not scan this sub" title="Do not scan this sub" ' +
+			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',0)"' +
+			'>' +
+			'<span class="glyphicon glyphicon-ban-circle"></span>' +
+			'</button>'
+		);
+	}
+
+	try {
+		var threads = getMessageThreads(http_request.query.sub[0]);
+		writeln('<div id="forum-list-container" class="list-group">');
+		threads.order.forEach(function(t){writeThread(threads.thread[t]);});
+		writeln('</div>');
+	} catch(err) {
+		log(err);
+	}
+
+} else if(
+	typeof http_request.query.group != "undefined"
+	&&
+	typeof msg_area.grp_list[http_request.query.group[0]] != "undefined"
+) {
+
+	// Sub list
+	var writeSub = function(sub) {
+		writeln(
+			format(
+				'<a href="./?page=%s&amp;sub=%s" class="list-group-item striped%s">' +
+				'<h4><strong>%s</strong></h4>' +
+				'<span title="Unread messages" class="badge %s" id="badge-%s"></span>' +
+				'<p>%s</p>' +
+				'</a>',
+				http_request.query.page[0],
+				sub.code,
+				((sub.scan_cfg&SCAN_CFG_NEW) || (sub.scan_cfg&SCAN_CFG_YONLY) ? " scanned" : ""),
+				sub.name,
+				((sub.scan_cfg&SCAN_CFG_NEW) || (sub.scan_cfg&SCAN_CFG_YONLY) ? "scanned" : "total"),
+				sub.code,
+				sub.description
+			)
+		);
+	}
+
+	var writeApiCall = function(subs) {
+		writeln('<script type="text/javascript">');
+		var codes = [];
+		subs.forEach(function(sub) { codes.push(sub.code); });
+		codes = codes.join('&sub=');
+		writeln('getSubUnreadCount("' + codes + '");');
+		writeln('setInterval(function(){getSubUnreadCount("' + codes + '");}, 60000);');
+		writeln('</script>');
+	}
+
+	writeln(
+		format(
+			'<ol class="breadcrumb">' +
+			'<li><a href="./?page=%s">Forum</a></li>' +
+			'<li><a href="./?page=%s&amp;group=%s">%s</a></li>' +
+			'</ol>',
+			http_request.query.page[0],
+			http_request.query.page[0],
+			http_request.query.group[0],
+			msg_area.grp_list[http_request.query.group[0]].name
+		)
+	);
+
+	try {
+		var subs = listSubs(http_request.query.group[0]);
+		writeln('<div id="forum-list-container" class="list-group">');
+		subs.forEach(writeSub);
+		writeln('</div>');
+		if(user.number > 0 && user.alias != settings.guest)
+			writeApiCall(subs);
+	} catch(err) {
+		log(err);
+	}
+
+} else {
+
+	// Group list
+	var writeGroup = function(group) {
+		writeln(
+			format(
+				'<a href="./?page=%s&amp;group=%s" class="list-group-item striped">' +
+				'<h3><strong>%s</strong></h3>' +
+				'<span title="Unread messages (other)" class="badge ignored" id="badge-ignored-%s"></span>' +
+				'<span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-%s"></span>' +
+				'<p>%s : %s sub-boards</p>' +
+				'</a>',
+				http_request.query.page[0],
+				group.index,
+				group.name,
+				group.index,
+				group.index,
+				group.description,
+				group.sub_list.length
+			)
+		);
+	}
+
+	var writeApiCall = function(groups) {
+		writeln('<script type="text/javascript">');
+		var indexes = [];
+		groups.forEach(function(group) { indexes.push(group.index); });
+		indexes = indexes.join('&group=');
+		writeln('getGroupUnreadCount("' + indexes + '");');
+		writeln('setInterval(function(){getGroupUnreadCount("' + indexes + '");},60000);');
+		writeln('</script>');
+	}
+	
+	writeln(
+		'<ol class="breadcrumb">' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '">Forum</a>' +
+		'</li>' +
+		'</ol>'
+	);
+
+	try {
+		var groups = listGroups();
+		writeln('<div id="forum-list-container" class="list-group">');
+		groups.forEach(writeGroup);
+		writeln('</div>');
+		if(user.number > 0 && user.alias != settings.guest)
+			writeApiCall(groups);
+	} catch(err) {
+		log(err);
+	}
+
+}
\ No newline at end of file
diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
new file mode 100644
index 0000000000..88c0d49124
--- /dev/null
+++ b/web/root/pages/002-files.ssjs
@@ -0,0 +1,118 @@
+//Files
+
+if(typeof argv[0] != "boolean" || !argv[0])
+	exit();
+
+load(system.exec_dir + "../web/lib/init.js");
+load(settings.web_lib + "files.js");
+
+//writeln('<script type="text/javascript" src="./js/files.js"></script>');
+
+if(typeof http_request.query.dir != "undefined") {
+	// File listing
+
+	writeln(
+		'<ol class="breadcrumb">' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
+		'</li>' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + file_area.dir[http_request.query.dir[0]].lib_index + '">' + file_area.dir[http_request.query.dir[0]].lib_name + '</a>' +
+		'</li>' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;dir=' + http_request.query.dir[0] + '">' + http_request.query.dir[0] + '</a>' +
+		'</li>' +
+		'</ol>'
+	);
+
+	var writeFileDetails = function(file) {
+		writeln(
+			format(
+				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
+				'<strong>%s</strong> (%s)' +
+				'<p><em>Uploaded %s</em></p>' +
+				'<p>%s</p>' +
+				'%s' +
+				'</a>',
+				http_request.query.dir[0],
+				file.name,
+				file.name,
+				file.size,
+				system.timestr(file.uploadDate),
+				file.description,
+				(file.extendedDescription == "" ? "" : ("<p>" + file.extendedDescription + "</p>"))
+			)
+		);
+	}
+
+	writeln('<div id="file-list-container" class="list-group">');
+	listFiles(http_request.query.dir[0]).forEach(writeFileDetails);
+	writeln('</div>');
+
+
+} else if(typeof http_request.query.library != "undefined") {
+	// File directory listing
+
+	writeln(
+		'<ol class="breadcrumb">' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
+		'</li>' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + http_request.query.library[0] + '">' + file_area.lib_list[http_request.query.library[0]].name + '</a>' +
+		'</li>' +
+		'</ol>'
+	);
+
+	var writeDirectory = function(dir) {
+		writeln(
+			format(
+				'<a href="./?page=%s&amp;dir=%s" class="list-group-item striped">' +
+				'<h4><strong>%s</strong></h4>' +
+				'<p>%s : %s files</p>' +
+				'</a>',
+				http_request.query.page[0],
+				dir.dir.code,
+				dir.dir.name,
+				dir.dir.description,
+				dir.fileCount
+			)
+		);
+	}
+
+	writeln('<div id="file-list-container" class="list-group">');
+	listDirectories(http_request.query.library[0]).forEach(writeDirectory);
+	writeln('</div>');	
+
+} else {
+	// File library listing
+
+	writeln(
+		'<ol class="breadcrumb">' +
+		'<li>' +
+		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
+		'</li>' +
+		'</ol>'
+	);
+
+	var writeLibrary = function(library) {
+		writeln(
+			format(
+				'<a href="./?page=%s&amp;library=%s" class="list-group-item striped">' +
+				'<h3><strong>%s</strong></h3>' +
+				'<p>%s : %s directories</p>' +
+				'</a>',
+				http_request.query.page[0],
+				library.index,
+				library.name,
+				library.description,
+				library.dir_list.length
+			)
+		);
+	}
+
+	writeln('<div id="file-list-container" class="list-group">');
+	listLibraries().forEach(writeLibrary);
+	writeln('</div>');
+
+}
\ No newline at end of file
diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
new file mode 100644
index 0000000000..93f6e7e3c0
--- /dev/null
+++ b/web/root/pages/003-games.xjs
@@ -0,0 +1,58 @@
+<!--Games-->
+<?xjs 
+	if(typeof argv[0] != "boolean" || !argv[0])
+		exit();
+	load(settings.web_lib + "ftelnet.js");
+?>
+
+<script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
+
+<a name="fTelnet"></a>
+<div id="fTelnetContainer" style="margin-bottom:1em;"></div>
+
+<script type="text/javascript">
+	fTelnet.Hostname = "<?xjs write(system.inet_addr); ?>";
+	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
+	fTelnet.ConnectionType = "telnet";
+	fTelnet.SplashScreen = "<?xjs write(getSplash()); ?>";
+	fTelnet.StatusBarVisible = false;
+	fTelnet.VirtualKeyboardVisible = (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
+	$(document).ready(fTelnet.Init());
+	var launchXtrn = function(code) {
+		$.getJSON(
+			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
+			function(data) {
+				fTelnet.Connect();
+			}
+		);
+	}
+</script>
+
+<div class="list-group">
+
+<?xjs
+
+	settings.xtrn_sections.forEach(
+		function(section) {
+			writeln('<div class="list-group-item">');
+			writeln('<h4>' + xtrn_area.sec[section].name + '</h4>');
+			writeln('<ul class="nav nav-pills">');
+			xtrn_area.sec_list[xtrn_area.sec[section].index].prog_list.forEach(
+				function(program) {
+					writeln(
+						'<li role="presentation">' +
+						'<a href="#fTelnet" onclick="launchXtrn(\'' + program.code + '\')">' +
+						program.name +
+						'</a>' +
+						'</li>'
+					);
+				}
+			);
+			writeln('</ul>');
+			writeln('</div>');
+		}
+	);
+
+?>
+
+</div>
\ No newline at end of file
diff --git a/web/root/pages/003_userlist.xjs b/web/root/pages/003_userlist.xjs
new file mode 100644
index 0000000000..34c2012288
--- /dev/null
+++ b/web/root/pages/003_userlist.xjs
@@ -0,0 +1,190 @@
+<!--User List-->
+<!--Slightly modified version of the ecWeb v3 userlist-->
+<?xjs
+	if(typeof argv[0] != "boolean" || !argv[0])
+		exit();
+
+	// Uncomment/comment properties below to enable/disable list columns
+	var columns = {
+//		number : { name : "#", type : "number" },
+		alias : { name : "Alias", type : "string" },
+//		name : { name : "Name", type : "string" },
+//		age : { name : "Age", type : "number" },
+//		gender : { name : "Sex", type : "string" },
+		location : { name : "Location", type : "string" },
+		laston_date : { name : "Last On", type : "date" },
+		connection : { name : "Via", type : "string" },
+//		firston_date : { name : "First On", type : "date" },
+//		total_logons : { name : "Calls", type : "number" },
+//		total_posts : { name : "Posts", type : "number" }
+	};
+
+	var pageSize = 500;
+
+	// Most people won't need to edit below this line
+	load("sbbsdefs.js");
+
+	if(	typeof http_request.query.offset == "undefined"
+		||
+		isNaN(parseInt(http_request.query.offset[0]))
+		||
+		http_request.query.offset[0] < 1
+	) {
+		var offset = 1;
+	} else {
+		var offset = parseInt(http_request.query.offset[0]);
+	}
+
+	var previousOffset = Math.max(1, offset - pageSize);
+	var nextOffset = Math.min(offset + pageSize, offset + (system.lastuser - offset));
+
+	var users = [];
+	var url = format(
+		"http://%s%s",
+		http_request.host,
+		http_request.request_string.split("&")[0]
+	);
+	
+	var sortUser = function(a, b, sortOrder, type) {
+		if(type == "string") {
+			a = a.toUpperCase();
+			b = b.toUpperCase();
+			ret =	(a < b)
+					?
+					((sortOrder == "ascending") ? 1 : -1)
+					:
+					(	(a > b)
+						?
+						((sortOrder == "ascending") ? -1 : 1)
+						:
+						0
+					);
+		} else if(type == "number" || type == "date") {
+			ret =	(a < b)
+					?
+					((sortOrder == "ascending") ? -1 : 1)
+					:
+					(	(a > b)
+						?
+						((sortOrder == "ascending") ? 1 : -1)
+						:
+						0
+					);
+		}
+		return ret;
+	}
+
+	var sortUsers = function(a, b) {
+		var ret = 0;
+		if(	typeof http_request.query.sortby != "undefined"
+			&&
+			typeof http_request.query.sortorder != "undefined"
+			&&
+			(	http_request.query.sortorder == "ascending"
+				||
+				http_request.query.sortorder == "descending"
+			)
+		) {
+			var sortBy = http_request.query.sortby.toString().toLowerCase();
+			var sortOrder = http_request.query.sortorder.toString().toLowerCase();
+			for(var c in columns) {
+				if(sortBy != c)
+					continue;
+				ret = sortUser(a[c], b[c], sortOrder, columns[c].type);
+				break;
+			}
+		}
+		return ret;
+	}
+	
+	var makeSortURLs = function(field, order) {
+		return format(
+			'<a class="icon" href="./?page=%s&sortby=%s&sortorder=ascending&offset=%s">'
+			+ '<span class="glyphicon glyphicon-arrow-up"></span>'
+			+ '</a>'
+			+ '<a class="icon" href="./?page=%s&sortby=%s&sortorder=descending&offset=%s">'
+			+ '<span class="glyphicon glyphicon-arrow-down"></span>'
+			+ '</a>',
+			http_request.query.page[0],
+			field,
+			offset,
+			http_request.query.page[0],
+			field,
+			offset
+		);
+	}
+	
+	var copyProperties = function(source, dest) {
+		for(var property in source) {
+			if(typeof source[property] == "string" || typeof source[property] == "number")
+				dest[property] = source[property];
+		}
+		return dest;
+	}
+
+	for(var u = offset;
+		u < ((system.lastuser - offset > pageSize) ? offset + pageSize : system.lastuser);
+		u++
+	) {
+		var usr1 = new User(u);
+		if(usr1.settings&USER_DELETED || usr1.compare_ars("REST Q"))
+			continue;
+		var usr2 = copyProperties(usr1, {});
+		usr2 = copyProperties(usr1.stats, usr2);	
+		users.push(usr2);
+	}
+	users.sort(sortUsers);
+?>
+
+<div class="well well-sm"><h3>User List</h3></div>
+<table class="table table-striped">
+	<thead>
+		<tr>
+<?xjs
+	for(var c in columns)
+		writeln(format("<th>%s %s</th>", columns[c].name, makeSortURLs(c)));
+?>
+		</tr>
+	</thead>
+	<tbody>
+<?xjs
+	for(var u = 0; u < users.length; u++) {
+		writeln('<tr>');
+		for(var c in columns) {
+			writeln(
+				'<td>' +
+				(columns[c].type == "date" ? system.timestr(users[u][c]) : users[u][c].toString()) +
+				'</td>'
+			);
+		}
+		writeln("</tr>");
+	}
+?>
+	</tbody>
+</table>
+
+<nav>
+	<ul class="pager">
+<?xjs
+	if(offset > 1) {
+		writeln(
+			format(
+				'<li><a href="./?page=%s&offset=%s">Previous</a></li>',
+				http_request.query.page[0],
+				previousOffset
+			)
+		);
+	}
+
+	if(system.lastuser - offset > pageSize) {
+		writeln(
+			format(
+				'<li><a href="./?page=%s&offset=%s">Next</a></li>',
+				http_request.query.page[0],
+				nextOffset
+			)
+		);
+	}
+?>
+	</ul>
+</nav>
\ No newline at end of file
diff --git a/web/root/pages/webctrl.ini b/web/root/pages/webctrl.ini
new file mode 100644
index 0000000000..f39b1514aa
--- /dev/null
+++ b/web/root/pages/webctrl.ini
@@ -0,0 +1,8 @@
+AccessRequirements = level 90
+Authorization = Digest
+
+[*userlist.xjs]
+AccessRequirements = LEVEL 50 AND REST NOT G
+
+[*games.xjs]
+AccessRequirements = LEVEL 50 AND REST NOT G
\ No newline at end of file
diff --git a/web/root/sidebar/001-nodelist.xjs b/web/root/sidebar/001-nodelist.xjs
new file mode 100644
index 0000000000..04722b392f
--- /dev/null
+++ b/web/root/sidebar/001-nodelist.xjs
@@ -0,0 +1,42 @@
+<div id="sbbs-nodelist"></div>
+<script type="text/javascript">
+var nodeList = function() {
+	$.getJSON(
+		"./api/system.ssjs?call=node-list",
+		function(data) {
+			$('#sbbs-nodelist').html(
+				'<h4>Who\'s online</h4>' +
+				'<table id="sbbs-nodelist-table" ' +
+				'class="table table-condensed table-responsive table-striped">' +
+				'<thead><tr><th>Node</th><th>Status</th></tr></thead>' +
+				'<tbody></tbody>' +
+				'</table>'
+			);
+			data.forEach(
+				function(n, index) {
+					var nll = <?xjs write(system.node_list.length); ?>;
+					$('#sbbs-nodelist-table').append(
+						'<tr>' +
+						'<th scope="row">' +
+						(index >= nll ? "W" : (index + 1)) +
+						'</th>' +
+						'<td id="nodelist-' + index + '">' +
+						(n.user == "" ? n.status : ('<strong>' + n.user + '</strong> ' + n.action)) +
+						'</td>' +
+						'</tr>'
+					);
+					if(n.user != "" && <?xjs write(user.alias!=settings.guest); ?>) {
+						$('#nodelist-' + index).attr('title', "Send a telegram");
+						$('#nodelist-' + index).css('cursor', "pointer");
+						$('#nodelist-' + index).click(
+							function() { sendTelegram(n.user); }
+						);
+					}
+				}
+			);
+		}
+	);
+}
+nodeList();
+setInterval(nodeList, 30000);
+</script>
\ No newline at end of file
diff --git a/web/root/sidebar/003-systemStats.xjs b/web/root/sidebar/003-systemStats.xjs
new file mode 100644
index 0000000000..2fac32bfae
--- /dev/null
+++ b/web/root/sidebar/003-systemStats.xjs
@@ -0,0 +1,57 @@
+<h4>System Info</h4>
+<table class="table table-condensed table-responsive table-striped">
+	<tbody>
+		<tr>
+			<th>Sysop:</th>
+			<td><?xjs write(system.operator); ?></td>
+		</tr>
+		<tr>
+			<th>Location:</th>
+			<td><?xjs write(system.location); ?></td>
+		</tr>
+		<tr>
+			<th>Users:</th>
+			<td><?xjs write(system.stats.total_users); ?></td>
+		</tr>
+		<tr>
+			<th>Nodes:</th>
+			<td><?xjs write(system.nodes); ?></td>
+		</tr>
+		<tr>
+			<th>Uptime:</th>
+			<td><?xjs write(system.secondstr(time() - system.uptime)); ?></td>
+		</tr>
+		<tr>
+			<th>Calls:</th>
+			<td><?xjs write(system.stats.total_logons); ?></td>
+		</tr>
+		<tr>
+			<th>Calls today:</th>
+			<td><?xjs write(system.stats.logons_today); ?></td>
+		</tr>
+		<tr>
+			<th>Files:</th>
+			<td><?xjs write(system.stats.total_files); ?></td>
+		</tr>
+		<tr>
+			<th>U/L today:</th>
+			<td><?xjs write(system.stats.files_uploaded_today); ?> files<br>
+				(<?xjs write(system.stats.bytes_uploaded_today); ?> bytes)
+			</td>
+		</tr>
+		<tr>
+			<th>D/L today:</th>
+			<td><?xjs write(system.stats.files_downloaded_today); ?> files<br>
+				(<?xjs write(system.stats.bytes_downloaded_today); ?> bytes)
+			</td>
+		</tr>
+		<tr>
+			<th>Messages:</th>
+			<td><?xjs write(system.stats.total_messages); ?></td>
+		</tr>
+		<tr>
+			<th>Posts today:</th>
+			<td><?xjs write(system.stats.messages_posted_today); ?></td>
+		</tr>
+	</tbody>
+</table>
-- 
GitLab


From 39eebe983f8e3a2fa13c5732df17f785f45e49b8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 12 Oct 2015 01:53:25 -0400
Subject: [PATCH 003/752] Early crappy readme

---
 README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/README.md b/README.md
index 92159fdbc5..1fc461afd5 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,52 @@
 # synchronet-web-v4
 A web interface for Synchronet BBS
+
+###Quick start
+
+- Back up your Synchronet installation
+- Shut down your BBS
+- Clone [or download an archive of](https://github.com/echicken/synchronet-web-v4/archive/master.zip) this repository to a convenient location
+	- Copy the contents of the downloaded *mods* directory into your local *mods* directory
+	- Copy the contents of the downloaded *text* directory into your local *text* directory
+	- Rename your current *web* directory to something like *web.old* and then copy the downloaded *web* directory in its place
+- [Download fTelnet](https://github.com/rickparrish/fTelnet/archive/master.zip)
+	- Extract the archive
+	- Copy the *release* subdirectory into your new *web/root/* directory
+	- Rename the copied *release* subdirectory to *ftelnet*
+- Add the following section to your *ctrl/modopts.ini* file:
+```ini
+[web]
+	; Unauthenticated visitors will be logged in as the user with this alias
+	; (Only give this user privileges you want unknown web visitors to have)
+	guest = Guest
+	; Login sessions expire after this many seconds of inactivity
+	timeout = 43200
+	; Users disappear from the "Who's online" list after this many seconds
+	inactivity = 900
+	; Allow new users to register via the web interface
+	user_registration = true
+	; Enforce a minimum password length if user_registration is true
+	minimum_password_length = 6
+	; Limit the length of a telegram (in characters) that a web user can send
+	maximum_telegram_length = 800
+	; Which external program sections to list on the Games page (comma-separated)
+	xtrn_sections = games,coa,programs
+	; Where (absolute or relative to 'exec') the 'lib' and 'root' directories live
+	web_directory = ../web
+	; Path to a .ans file to use as the ftelnet splash screen
+	ftelnet_splash = ../text/synch.ans
+```
+- Add the following section to your *ctrl/services.ini* file if it isn't there already:
+```ini
+[WebSocket]
+Port=1123
+Options=NO_HOST_LOOKUP
+Command=websocketservice.js
+```
+- Add the following section to your *ctrl/services.ini* file:
+```ini
+[WebSocketRLogin]
+Port=1513
+Options=NO_HOST_LOOKUP
+Command=websocket-rlogin-service.js
+```
\ No newline at end of file
-- 
GitLab


From 9c44fddeab7d5632807e6146b403462020a32863 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 12 Oct 2015 02:33:37 -0400
Subject: [PATCH 004/752] Early crappy readme

---
 README.md | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 1fc461afd5..7ffc229903 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,16 @@
 # synchronet-web-v4
 A web interface for Synchronet BBS
 
+###Disclaimer
+
+Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
+
+###Requirements
+
+- This web interface has only been tested with Synchronet BBS 3.16c.  It will probably work with earlier and later versions.
+
+- The *Files* page of this web interface relies on a script which was introduced *after* the release of Synchronet BBS 3.16c.  You can grab a copy of *filedir.js* [here](http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/filedir.js?revision=1.2), and you should place it in your *exec/load/* directory.
+
 ###Quick start
 
 - Back up your Synchronet installation
@@ -30,7 +40,7 @@ A web interface for Synchronet BBS
 	; Limit the length of a telegram (in characters) that a web user can send
 	maximum_telegram_length = 800
 	; Which external program sections to list on the Games page (comma-separated)
-	xtrn_sections = games,coa,programs
+	xtrn_sections = games,puzzle,rpg,erotic
 	; Where (absolute or relative to 'exec') the 'lib' and 'root' directories live
 	web_directory = ../web
 	; Path to a .ans file to use as the ftelnet splash screen
@@ -49,4 +59,17 @@ Command=websocketservice.js
 Port=1513
 Options=NO_HOST_LOOKUP
 Command=websocket-rlogin-service.js
-```
\ No newline at end of file
+```
+- Tell your router and firewall to open and foreward ports *1123* and *1513* to your BBS
+
+###Configuration
+
+- Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
+- Look at those *xtrn_sections* in the [web] section of *ctrl/modopts.ini*.  They should be the *internal codes* of all External Programs sections that you want to make available to authenticated users via the web.  (You probably don't have an *erotic* External Programs area, but if you do ... that's cool.)
+
+###Uninstall
+
+- To stop using this web interface, you can just revert to your previous *web* directory at any time.
+- The [web] section added to *ctrl/modopts.ini* won't hurt anything if you leave it there, but you can delete it if you want
+- Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
+- Undo any changes you made to your firewall & router during the *Quick Start*
\ No newline at end of file
-- 
GitLab


From c6991cbf3a93d206b49c9bcea9f85dfc7716a370 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 12 Oct 2015 02:52:15 -0400
Subject: [PATCH 005/752] Early crappy readme

---
 README.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/README.md b/README.md
index 7ffc229903..464591f09f 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ Use this software at your own risk.  It's still being developed, and hasn't been
 
 ###Quick start
 
+I haven't actually tried these instructions on a clean Synchronet BBS installation, but they should work.
+
 - Back up your Synchronet installation
 - Shut down your BBS
 - Clone [or download an archive of](https://github.com/echicken/synchronet-web-v4/archive/master.zip) this repository to a convenient location
@@ -67,6 +69,14 @@ Command=websocket-rlogin-service.js
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
 - Look at those *xtrn_sections* in the [web] section of *ctrl/modopts.ini*.  They should be the *internal codes* of all External Programs sections that you want to make available to authenticated users via the web.  (You probably don't have an *erotic* External Programs area, but if you do ... that's cool.)
 
+###Customization
+
+- This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
+	- You can place your own CSS overrides in *web/root/css/style.css*
+	- You can load another stylesheet of your own choosing in the <head> section of *web/root/index.xjs* (load it after the others)
+- The sidebar module & page structure is *mostly* similar to the system used in ecWeb v3.  See [the old instructions](http://wiki.synchro.net/howto:ecweb#the_sidebar) for info on adding content.
+	- You can force a link to a page to be placed in the *More* menu by using an underscore as a separator in its filename rather than a hyphen
+
 ###Uninstall
 
 - To stop using this web interface, you can just revert to your previous *web* directory at any time.
-- 
GitLab


From b070a5486d28101df92f5bcab0cf9f0ba34f94a3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 12 Oct 2015 02:56:47 -0400
Subject: [PATCH 006/752] Early crappy readme

---
 README.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 464591f09f..33f75af5c0 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,11 @@ A web interface for Synchronet BBS
 
 ###Disclaimer
 
-Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
+- Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
+
+- This readme kind of sucks.  I'll put a better one on the Synchronet Wiki once I'm ready to bring this over to the Synchronet CVS repository.
+	- Things may change quite a bit by the time I do bring this web interface over to the CVS, so try not to get too attached to any customizations that you make in the meantime.
+		- However, if you're an early adopter, I would appreciate your feedback.
 
 ###Requirements
 
-- 
GitLab


From 727ef07ba8cb23544979a6ac09288f2b633878b7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Nov 2015 17:49:45 -0500
Subject: [PATCH 007/752] Pass message body through word_wrap before saving to
 MsgBase.  Code cleanup.

---
 web/lib/forum.js | 335 +++++++++++++++++++++++------------------------
 1 file changed, 162 insertions(+), 173 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 594619edb8..ae81238311 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,23 +1,22 @@
-load("sbbsdefs.js");
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "mime-decode.js");
+load('sbbsdefs.js');
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'mime-decode.js');
 
-var listGroups = function() {
+function listGroups () {
 	var response = [];
 	msg_area.grp_list.forEach(
-		function(grp) {
-			if(grp.sub_list.length > 0)
-				response.push(grp);
+		function (grp) {
+			if (grp.sub_list.length > 0) response.push(grp);
 		}
 	);
 	return response;
 }
 
 // Returns an array of objects of "useful" information about subs
-var listSubs = function(group) {
+function listSubs (group) {
 	var response = [];
 	msg_area.grp_list[group].sub_list.forEach(
-		function(sub) {
+		function (sub) {
 			response.push(
 				{	'index' : sub.index,
 					'code' : sub.code,
@@ -47,27 +46,25 @@ var listSubs = function(group) {
 	return response;
 }
 
-var getSubUnreadCount = function(sub) {
+function getSubUnreadCount (sub) {
 	var ret = {
 		'scanned' : 0,
 		'total' : 0
 	};
-	if(typeof msg_area.sub[sub] == "undefined")
-		return ret;
+	if (typeof msg_area.sub[sub] === 'undefined') return ret;
 	try {
 		var msgBase = new MsgBase(sub);
 		msgBase.open();
-		for(var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
+		for (var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
 			var i = msgBase.get_msg_index(m);
-			if(i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP)
-				continue;
-			if(	(	(msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY)
+			if (i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP) continue;
+			if ((	(msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY)
 					&&
-					i.to == crc16_calc(user.alias.toLowerCase())
+					i.to === crc16_calc(user.alias.toLowerCase())
 					||
-					i.to == crc16_calc(user.name.toLowerCase())
+					i.to === crc16_calc(user.name.toLowerCase())
 					||
-					(sub == 'mail' && i.to == crc16_calc(user.number))
+					(sub === 'mail' && i.to === crc16_calc(user.number))
 				)
 				||
 				(msg_area.sub[sub].scan_cfg&SCAN_CFG_NEW)
@@ -77,21 +74,20 @@ var getSubUnreadCount = function(sub) {
 			ret.total++;
 		}
 		msgBase.close();
-	} catch(err) {
+	} catch (err) {
 		log(err);
 	}
 	return ret;
 }
 
-var getGroupUnreadCount = function(group) {
+function getGroupUnreadCount (group) {
 	var ret = {
 		'scanned' : 0,
 		'total' : 0
 	};
-	if(typeof msg_area.grp_list[group] == "undefined")
-		return count;
+	if (typeof msg_area.grp_list[group] === 'undefined') return count;
 	msg_area.grp_list[group].sub_list.forEach(
-		function(sub) {
+		function (sub) {
 			var count = getSubUnreadCount(sub.code);
 			ret.scanned += count.scanned;
 			ret.total += count.total;
@@ -100,61 +96,63 @@ var getGroupUnreadCount = function(group) {
 	return ret;
 }
 
-var getUnreadInThread = function(sub, thread) {
+function getUnreadInThread (sub, thread) {
 	var count = 0;
 	thread.messages.forEach(
-		function(header) {
-			if(header.number > msg_area.sub[sub].scan_ptr)
-				count++;
+		function (header) {
+			if (header.number > msg_area.sub[sub].scan_ptr) count++;
 		}
 	);
 	return count;
 }
 
-var getMailUnreadCount = function() {
+function getMailUnreadCount () {
 	var count = 0;
 	var msgBase = new MsgBase('mail');
 	msgBase.open();
-	for(var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
+	for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
 		var index = msgBase.get_msg_header(m);
-		if(index === null)
-			continue;
-		if(index.to_ext != user.number)
-			continue;
-		if(index.attr&MSG_READ)
-			continue;
-		if(index.attr&MSG_DELETE)
-			continue;
+		if (index === null) continue;
+		if (index.to_ext !== user.number) continue;
+		if (index.attr&MSG_READ) continue;
+		if (index.attr&MSG_DELETE) continue;
 		count++;
 	}
 	msgBase.close();
 	return count;
 }
 
-var getMailHeaders = function(sent, ascending) {
-	if(typeof sent != "undefined" && sent && user.security.restrictions&UFLAG_K)
+function getMailHeaders (sent, ascending) {
+	if (typeof sent !== 'undefined' &&
+		sent &&
+		user.security.restrictions&UFLAG_K
+	) {
 		return []; // They'll just see nothing.  Provide actual feedback?  Does anyone use REST K?
+	}
 	var headers = [];
 	var msgBase = new MsgBase('mail');
-	if(!msgBase.open())
-		return headers;
-	for(var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
+	if (!msgBase.open()) return headers;
+	for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
 		var h = msgBase.get_msg_header(m);
-		if(h === null || h.attr&MSG_DELETE)
+		if (h === null || h.attr&MSG_DELETE) continue;
+		if (	(typeof sent != 'undefined' && sent) &&
+				h.from_ext != user.number
+		) {
 			continue;
-		if((typeof sent != "undefined" && sent) && h.from_ext != user.number)
-			continue;
-		else if((typeof sent == "undefined" || !sent) && h.to_ext != user.number)
+		} else if (
+			(typeof sent == 'undefined' || !sent) &&
+			h.to_ext != user.number
+		) {
 			continue;
+		}
 		headers.push(h);
 	}
 	msgBase.close();
-	if(typeof ascending == "undefined" || !ascending)
-		headers.reverse();
+	if (typeof ascending === 'undefined' || !ascending) headers.reverse();
 	return headers;
 }
 
-var mimeDecode = function(header, body, code) {
+function mimeDecode (header, body, code) {
 	var ret = {
 		'type' : "",
 		'body' : [],
@@ -162,9 +160,9 @@ var mimeDecode = function(header, body, code) {
 		'attachments' : []
 	};
 	var msg = mime_decode(header, body, code);
-	if(typeof msg.inlines != "undefined") {
+	if (typeof msg.inlines !== 'undefined') {
 		msg.inlines.forEach(
-			function(inline) {
+			function (inline) {
 				ret.inlines.push(
 					format(
 						'<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;cid=%s" target="_blank">%s</a>',
@@ -174,9 +172,9 @@ var mimeDecode = function(header, body, code) {
 			}
 		);
 	}
-	if(typeof msg.attachments != "undefined") {
+	if (typeof msg.attachments !== 'undefined') {
 		msg.attachments.forEach(
-			function(attachment) {
+			function (attachment) {
 				ret.attachments.push(
 					format(
 						'<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;filename=%s" target="_blank">%s</a>',
@@ -191,35 +189,36 @@ var mimeDecode = function(header, body, code) {
 	return ret;
 }
 
-var getMailBody = function(number) {
+function getMailBody (number) {
 
 	var ret = {
-		'type' : "",
-		'body' : "",
+		'type' : '',
+		'body' : '',
 		'inlines' : [],
 		'attachments' : []
 	};
 
 	number = Number(number);
-	if(isNaN(number) || number < 0)
-		return ret;
+	if (isNaN(number) || number < 0) return ret;
 
 	var msgBase = new MsgBase('mail');
-	if(!msgBase.open())
-		return ret;
+	if (!msgBase.open()) return ret;
 	var header = msgBase.get_msg_header(number);
-	if(header !== null && (header.to_ext == user.number || header.from_ext == user.number)) {
+	if (header !== null	&&
+		(	header.to_ext === user.number ||
+			header.from_ext === user.number
+		)
+	) {
 		var body = msgBase.get_msg_body(false, number, header);
-		if(header.to_ext == user.number && (header.attr^MSG_READ)) {
+		if (header.to_ext === user.number && (header.attr^MSG_READ)) {
 			header.attr|=MSG_READ;
 			msgBase.put_msg_header(false, number, header);
 		}
 	}
 	msgBase.close();
-	if(typeof body == "undefined" || body === null)
-		return ret;
+	if (typeof body === 'undefined' || body === null) return ret;
 
-	var decoded = mimeDecode(header, body, "mail");
+	var decoded = mimeDecode(header, body, 'mail');
 	ret.type = decoded.type;
 	ret.body = formatMessage(decoded.body);
 	ret.inlines = decoded.inlines;
@@ -229,12 +228,11 @@ var getMailBody = function(number) {
 }
 
 // Returns the user's signature, or an empty String
-var getSignature = function() {
-	var fn = format("%s/user/%04d.sig", system.data_dir, user.number);
-	if(!file_exists(fn))
-		return "";
+function getSignature () {
+	var fn = format('%s/user/%04d.sig', system.data_dir, user.number);
+	if (!file_exists(fn)) return "";
 	var f = new File(fn);
-	f.open("r");
+	f.open('r');
 	var signature = f.read();
 	f.close();
 	return signature;
@@ -242,57 +240,42 @@ var getSignature = function() {
 
 // Post a messge to 'sub'
 // Called by postNew/postReply, not directly
-var postMessage = function(sub, header, body) {
+function postMessage (sub, header, body) {
 	var ret = false;
-	if(	user.alias == settings.guest
-		||
-		typeof msg_area.sub[sub] == "undefined"
-		||
-		!msg_area.sub[sub].can_post
-		||
-		typeof header.to != "string"
-		||
-		header.to == ""
-		||
-		typeof header.from != "string"
-		||
-		typeof header.subject != "string"
+	if (user.alias === settings.guest ||
+		typeof msg_area.sub[sub] === 'undefined' ||
+		!msg_area.sub[sub].can_post	||
+		typeof header.to !== 'string' ||
+		header.to === '' ||
+		typeof header.from !== 'string'	||
+		typeof header.subject !== 'string' ||
 // This could be a reply to a message with no subject, apparently
-//		||
-//		header.subject == ""
-		||
-		typeof body != "string"
-		||
-		body == ""
+//		header.subject == '' ||
+		typeof body !== 'string' ||
+		body === ''
 	) {
 		return ret;
 	}
 	try {
 		var msgBase = new MsgBase(sub);
 		msgBase.open();
-		ret = msgBase.save_msg(header, body);
+		ret = msgBase.save_msg(header, word_wrap(body));
 		msgBase.close();
-	} catch(err) {
+	} catch (err) {
 		log(err);
 	}
 	return ret;
 }
 
 // Post a new (non-reply) message to 'sub'
-var postNew = function(sub, to, subject, body) {
-	if(	typeof sub != "string"
-		||
-		typeof to != "string"
-		||
-		to == ""
-		||
-		typeof subject != "string"
-		||
-		subject == ""
-		||
-		typeof body != "string"
-		||
-		body == ""
+function postNew (sub, to, subject, body) {
+	if (typeof sub !== 'string' ||
+		typeof to !== 'string' ||
+		to === '' ||
+		typeof subject !== 'string' ||
+		subject === '' ||
+		typeof body !== 'string' ||
+		body === ''
 	) {
 		return false;
 	}
@@ -301,48 +284,56 @@ var postNew = function(sub, to, subject, body) {
 		'from' : user.alias,
 		'subject' : subject
 	};
-	if(sub == "mail")
+	if (sub === 'mail') {
 		return postMail(header, body);
-	else
+	} else {
 		return postMessage(sub, header, body);
+	}
 }
 
 // Post a message to the mail sub, if this user can do so
 // Called by postNew/postReply, not directly
-var postMail = function(header, body) {
+function postMail (header, body) {
 	// Lazy ARS checks; we could check the *type* of email being sent, I guess.
-	if(user.security.restrictions&UFLAG_E || user.security.restrictions&UFLAG_M)
+	if (user.security.restrictions&UFLAG_E || user.security.restrictions&UFLAG_M) {
 		return false;
-	if(typeof header.to != "string" || typeof header.subject != "string" || typeof body != "string")
+	}
+	if (	typeof header.to !== 'string' ||
+			typeof header.subject !== 'string' ||
+			typeof body !== 'string'
+	) {
 		return false;
+	}
 	var ret = false;
-	if(user.number < 1 || user.alias == settings.guest)
-		return ret;
+	if (user.number < 1 || user.alias === settings.guest) return ret;
 	var na = netaddr_type(header.to);
-	if(na > 0) {
+	if (na > 0) {
 		header.to_net_type = na;
 		header.to_net_addr = header.to;
 	}
 	var msgBase = new MsgBase('mail');
-	if(msgBase.open()) {
-		ret = msgBase.save_msg(header, body);
+	if (msgBase.open()) {
+		ret = msgBase.save_msg(header, word_wrap(body));
 		msgBase.close();
 	}
 	return ret;
 }
 
 // Add a new message to 'sub' in reply to parent message 'pid'
-var postReply = function(sub, body, pid) {
+function postReply (sub, body, pid) {
 	var ret = false;
-	if(typeof sub != "string" || typeof body != "string" || typeof pid != "number")
+	if (	typeof sub !== 'string' ||
+			typeof body !== 'string' ||
+			typeof pid !== 'number'
+	) {
 		return ret;
+	}
 	try {
 		var msgBase = new MsgBase(sub);
 		msgBase.open();
 		var pHeader = msgBase.get_msg_header(pid);
 		msgBase.close();
-		if(pHeader === null)
-			return ret;
+		if (pHeader === null) return ret;
 		var header = {
 			'to' : pHeader.from,
 			'to_net_addr' : pHeader.from_net_addr,
@@ -350,11 +341,12 @@ var postReply = function(sub, body, pid) {
 			'subject' : pHeader.subject,
 			'thread_back' : pHeader.number
 		};
-		if(sub == 'mail')
+		if (sub === 'mail') {
 			ret = postMail(header, body);
-		else
+		} else {
 			ret = postMessage(sub, header, body);
-	} catch(err) {
+		}
+	} catch (err) {
 		log(err);
 	}
 	return ret;
@@ -363,46 +355,47 @@ var postReply = function(sub, body, pid) {
 // Delete a message if
 // - This is the mail sub, and the message was sent by or to this user
 // - This is another sub on which the user is an operator
-var deleteMessage = function(sub, number) {
+function deleteMessage (sub, number) {
 	number = parseInt(number);
-	if(typeof msg_area.sub[sub] == "undefined" && sub != "mail")
+	if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail')	{
 		return false;
+	}
 	var msgBase = new MsgBase(sub);
-	if(!msgBase.open())
-		return false;
+	if (!msgBase.open()) return false;
 	var header = msgBase.get_msg_header(number);
-	if(header === null)
-		return false;
-	if(sub == 'mail' && (header.to_ext == user.number || header.from_ext == user.number))
+	if (header === null) return false;
+	if (	sub === 'mail' &&
+			(header.to_ext === user.number || header.from_ext === user.number)
+	) {
 		var ret = msgBase.remove_msg(number);
-	else if(sub != 'mail' && msg_area.sub[sub].is_operator)
+	} else if (sub !== 'mail' && msg_area.sub[sub].is_operator) {
 		var ret = msgBase.remove_msg(number);
-	else
+	} else {
 		var ret = false;
+	}
 	msgBase.close();
 	return ret;
 }
 
 // Deuce's URL-ifier
-var linkify = function(body) {
-	urlRE=/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
-	body=body.replace(
+function linkify (body) {
+	urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
+	body = body.replace(
 		urlRE, 
-		function(str) {
+		function (str) {
 			var ret=''
 			var p=0;
 			var link=str.replace(/\.*$/, '');
 			var linktext=link;
-			if(link.indexOf('://')==-1)
-				link='mailto:'+link;
-			return('<a class="ulLink" href="'+link+'">'+linktext+'</a>'+str.substr(linktext.length));
+			if (link.indexOf('://') === -1) link = 'mailto:' + link;
+			return ('<a class="ulLink" href="' + link + '">' + linktext + '</a>' + str.substr(linktext.length));
 		}
 	);
-	return(body);
+	return (body);
 }
 
 // Somewhat modified version of Deuce's "magical quoting stuff" from v3
-var quotify = function(body) {
+function quotify (body) {
 
 	var blockquote_start = '<blockquote>';
 	var blockquote_end = '</blockquote>';
@@ -413,12 +406,12 @@ var quotify = function(body) {
 	var quote_depth=0;
 	var prefixes = [];
 
-	for(l in lines) {
+	for (l in lines) {
 
 		var line_prefix = '';
 		var m = lines[l].match(/^((?:\s?[^\s]{0,3}&gt;\s?)+)/);
 
-		if(m !== null) {
+		if (m !== null) {
 
 			var new_prefixes = m[1].match(/\s?[^\s]{0,3}&gt;\s?/g);
 			var p;
@@ -427,29 +420,28 @@ var quotify = function(body) {
 			line = lines[l];
 			
 			// If the new length is smaller than the old one, close the extras
-			for(p = new_prefixes.length; p < prefixes.length; p++) {
-				if(quote_depth < 1)
-					continue;
+			for (p = new_prefixes.length; p < prefixes.length; p++) {
+				if (quote_depth < 1) continue;
 				line_prefix = line_prefix + blockquote_end;
 				quote_depth--;
 			}
 
-			for(p in new_prefixes) {
+			for (p in new_prefixes) {
 				// Remove prefix from start of line
 				line = line.substr(new_prefixes[p].length);
 
-				if(typeof prefixes[p] == "undefined") {
+				if (typeof prefixes[p] === "undefined") {
 					/* New depth */
 					line_prefix = line_prefix + blockquote_start;
 					quote_depth++;
-				} else if(broken) {
+				} else if (broken) {
 					line_prefix = line_prefix + blockquote_start;
 					quote_depth++;
-				} else if(prefixes[p].replace(/^\s*(.*?)\s*$/,"$1") != new_prefixes[p].replace(/^\s*(.*?)\s*$/,"$1")) {
+				} else if (prefixes[p].replace(/^\s*(.*?)\s*$/,"$1") != new_prefixes[p].replace(/^\s*(.*?)\s*$/,"$1")) {
 					// Close all remaining old prefixes and start one new one
 					var o;
-					for(o = p; o < prefixes.length && o < new_prefixes.length; o++) {
-						if(quote_depth > 0) {
+					for (o = p; o < prefixes.length && o < new_prefixes.length; o++) {
+						if (quote_depth > 0) {
 							line_prefix = blockquote_end + line_prefix;
 							quote_depth--;
 						}
@@ -465,9 +457,8 @@ var quotify = function(body) {
 
 		} else {
 
-			for(p = 0; p < prefixes.length; p++) {
-				if(quote_depth < 1)
-					continue;
+			for (p = 0; p < prefixes.length; p++) {
+				if (quote_depth < 1) continue;
 				line_prefix = line_prefix + blockquote_end;
 				quote_depth--;
 			}
@@ -480,9 +471,10 @@ var quotify = function(body) {
 
 	}
 
-	if(quote_depth != 0) {
-		for(;quote_depth > 0; quote_depth--)
+	if (quote_depth !== 0) {
+		for (;quote_depth > 0; quote_depth--) {
 			body += blockquote_end;
+		}
 	}
 
 	return body.replace(/\<\/blockquote\>\r\n<blockquote\>/g, "\r\n");
@@ -490,14 +482,13 @@ var quotify = function(body) {
 }
 
 // Format message body for the web
-var formatMessage = function(body, ansi) {
+function formatMessage (body, ansi) {
 
 	// Workaround for html_encode(body, true, false, false, false);
 	// which causes a crash if body is empty
-	if(body == "")
-		return body;
+	if (body === '') return body;
 
-	if(typeof ansi == "boolean" && ansi) {
+	if (typeof ansi === 'boolean' && ansi) {
 
 		body = html_encode(body, true, false, true, true);
 		body = body.replace(/\r?\n+(<\/span>)?$/,'$1');
@@ -505,12 +496,12 @@ var formatMessage = function(body, ansi) {
 
 		// Get the last line
 		var body_m = body.match(/\n([^\n]*)$/);
-		if(body_m != null) {
+		if (body_m !== null) {
 			body = '<pre>'+body;
 			body_m[1] = body_m[1].replace(/&[^;]*;/g,".");
 			body_m[1] = body_m[1].replace(/<[^>]*>/g,"");
 			var lenremain = 80 - body_m[1].length;
-			while(lenremain > 0) {
+			while (lenremain > 0) {
 				body += '&nbsp;';
 				lenremain--;
 			}
@@ -518,8 +509,9 @@ var formatMessage = function(body, ansi) {
 		} else {
 			/* If we couldn't get the last line, add a line of 80 columns */
 			var line = "";
-			for(n = 0; n < 80; n++)
-				line += "&nbsp;";
+			for (n = 0; n < 80; n++) {
+				line += '&nbsp;';
+			}
 			body = '<pre>' + body + line + "</pre>";
 		}
 
@@ -547,7 +539,7 @@ var formatMessage = function(body, ansi) {
 
 }
 
-var setScanCfg = function(sub, cfg) {
+function setScanCfg (sub, cfg) {
 
 	var opts = [
 		0,
@@ -555,15 +547,12 @@ var setScanCfg = function(sub, cfg) {
 		SCAN_CFG_YONLY
 	];
 
-	if(typeof msg_area.sub[sub] == "undefined")
-		return false;
+	if (typeof msg_area.sub[sub] === 'undefined') return false;
 
 	cfg = parseInt(cfg);
-	if(isNaN(cfg) || cfg < 0 || cfg > 2)
-		return false;
+	if (isNaN(cfg) || cfg < 0 || cfg > 2) return false;
 
-	if(cfg == 2)
-		opts[cfg]|=SCAN_CFG_NEW;
+	if (cfg === 2) opts[cfg]|=SCAN_CFG_NEW;
 
 	msg_area.sub[sub].scan_cfg = opts[cfg];
 	return true;
-- 
GitLab


From b2f3820cd8f290f9797761f325866b112fc8e5ad Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Nov 2015 17:51:37 -0500
Subject: [PATCH 008/752] Code cleanup.

---
 web/lib/forum.js | 948 +++++++++++++++++++++++------------------------
 1 file changed, 474 insertions(+), 474 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index ae81238311..e67ceb07aa 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -3,558 +3,558 @@ load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
 
 function listGroups () {
-	var response = [];
-	msg_area.grp_list.forEach(
-		function (grp) {
-			if (grp.sub_list.length > 0) response.push(grp);
-		}
-	);
-	return response;
+    var response = [];
+    msg_area.grp_list.forEach(
+        function (grp) {
+            if (grp.sub_list.length > 0) response.push(grp);
+        }
+    );
+    return response;
 }
 
 // Returns an array of objects of "useful" information about subs
 function listSubs (group) {
-	var response = [];
-	msg_area.grp_list[group].sub_list.forEach(
-		function (sub) {
-			response.push(
-				{	'index' : sub.index,
-					'code' : sub.code,
-					'grp_index' : sub.grp_index,
-					'grp_name' : sub.grp_name,
-					'name' : sub.name,
-					'description' : sub.description,
-					'qwk_name' : sub.qwk_name,
-					'qwk_conf' : sub.qwk_conf,
-					'qwk_tagline' : sub.qwknet_tagline,
-					'newsgroup' : sub.newsgroup,
-					'ars' : sub.ars,
-					'read_ars' : sub.read_ars,
-					'can_read' : sub.can_read,
-					'post_ars' : sub.post_ars,
-					'can_post' : sub.can_post,
-					'operator_ars' : sub.operator_ars,
-					'is_operator' : sub.is_operator,
-					'moderated_ars' : sub.moderated_ars,
-					'is_moderated' : sub.is_moderated,
-					'scan_ptr' : sub.scan_ptr,
-					'scan_cfg' : sub.scan_cfg
-				}
-			);
-		}
-	);
-	return response;
+    var response = [];
+    msg_area.grp_list[group].sub_list.forEach(
+        function (sub) {
+            response.push(
+                {   'index' : sub.index,
+                    'code' : sub.code,
+                    'grp_index' : sub.grp_index,
+                    'grp_name' : sub.grp_name,
+                    'name' : sub.name,
+                    'description' : sub.description,
+                    'qwk_name' : sub.qwk_name,
+                    'qwk_conf' : sub.qwk_conf,
+                    'qwk_tagline' : sub.qwknet_tagline,
+                    'newsgroup' : sub.newsgroup,
+                    'ars' : sub.ars,
+                    'read_ars' : sub.read_ars,
+                    'can_read' : sub.can_read,
+                    'post_ars' : sub.post_ars,
+                    'can_post' : sub.can_post,
+                    'operator_ars' : sub.operator_ars,
+                    'is_operator' : sub.is_operator,
+                    'moderated_ars' : sub.moderated_ars,
+                    'is_moderated' : sub.is_moderated,
+                    'scan_ptr' : sub.scan_ptr,
+                    'scan_cfg' : sub.scan_cfg
+                }
+            );
+        }
+    );
+    return response;
 }
 
 function getSubUnreadCount (sub) {
-	var ret = {
-		'scanned' : 0,
-		'total' : 0
-	};
-	if (typeof msg_area.sub[sub] === 'undefined') return ret;
-	try {
-		var msgBase = new MsgBase(sub);
-		msgBase.open();
-		for (var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
-			var i = msgBase.get_msg_index(m);
-			if (i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP) continue;
-			if ((	(msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY)
-					&&
-					i.to === crc16_calc(user.alias.toLowerCase())
-					||
-					i.to === crc16_calc(user.name.toLowerCase())
-					||
-					(sub === 'mail' && i.to === crc16_calc(user.number))
-				)
-				||
-				(msg_area.sub[sub].scan_cfg&SCAN_CFG_NEW)
-			) {
-				ret.scanned++;
-			}
-			ret.total++;
-		}
-		msgBase.close();
-	} catch (err) {
-		log(err);
-	}
-	return ret;
+    var ret = {
+        'scanned' : 0,
+        'total' : 0
+    };
+    if (typeof msg_area.sub[sub] === 'undefined') return ret;
+    try {
+        var msgBase = new MsgBase(sub);
+        msgBase.open();
+        for (var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
+            var i = msgBase.get_msg_index(m);
+            if (i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP) continue;
+            if ((   (msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY)
+                    &&
+                    i.to === crc16_calc(user.alias.toLowerCase())
+                    ||
+                    i.to === crc16_calc(user.name.toLowerCase())
+                    ||
+                    (sub === 'mail' && i.to === crc16_calc(user.number))
+                )
+                ||
+                (msg_area.sub[sub].scan_cfg&SCAN_CFG_NEW)
+            ) {
+                ret.scanned++;
+            }
+            ret.total++;
+        }
+        msgBase.close();
+    } catch (err) {
+        log(err);
+    }
+    return ret;
 }
 
 function getGroupUnreadCount (group) {
-	var ret = {
-		'scanned' : 0,
-		'total' : 0
-	};
-	if (typeof msg_area.grp_list[group] === 'undefined') return count;
-	msg_area.grp_list[group].sub_list.forEach(
-		function (sub) {
-			var count = getSubUnreadCount(sub.code);
-			ret.scanned += count.scanned;
-			ret.total += count.total;
-		}
-	);
-	return ret;
+    var ret = {
+        'scanned' : 0,
+        'total' : 0
+    };
+    if (typeof msg_area.grp_list[group] === 'undefined') return count;
+    msg_area.grp_list[group].sub_list.forEach(
+        function (sub) {
+            var count = getSubUnreadCount(sub.code);
+            ret.scanned += count.scanned;
+            ret.total += count.total;
+        }
+    );
+    return ret;
 }
 
 function getUnreadInThread (sub, thread) {
-	var count = 0;
-	thread.messages.forEach(
-		function (header) {
-			if (header.number > msg_area.sub[sub].scan_ptr) count++;
-		}
-	);
-	return count;
+    var count = 0;
+    thread.messages.forEach(
+        function (header) {
+            if (header.number > msg_area.sub[sub].scan_ptr) count++;
+        }
+    );
+    return count;
 }
 
 function getMailUnreadCount () {
-	var count = 0;
-	var msgBase = new MsgBase('mail');
-	msgBase.open();
-	for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
-		var index = msgBase.get_msg_header(m);
-		if (index === null) continue;
-		if (index.to_ext !== user.number) continue;
-		if (index.attr&MSG_READ) continue;
-		if (index.attr&MSG_DELETE) continue;
-		count++;
-	}
-	msgBase.close();
-	return count;
+    var count = 0;
+    var msgBase = new MsgBase('mail');
+    msgBase.open();
+    for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
+        var index = msgBase.get_msg_header(m);
+        if (index === null) continue;
+        if (index.to_ext !== user.number) continue;
+        if (index.attr&MSG_READ) continue;
+        if (index.attr&MSG_DELETE) continue;
+        count++;
+    }
+    msgBase.close();
+    return count;
 }
 
 function getMailHeaders (sent, ascending) {
-	if (typeof sent !== 'undefined' &&
-		sent &&
-		user.security.restrictions&UFLAG_K
-	) {
-		return []; // They'll just see nothing.  Provide actual feedback?  Does anyone use REST K?
-	}
-	var headers = [];
-	var msgBase = new MsgBase('mail');
-	if (!msgBase.open()) return headers;
-	for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
-		var h = msgBase.get_msg_header(m);
-		if (h === null || h.attr&MSG_DELETE) continue;
-		if (	(typeof sent != 'undefined' && sent) &&
-				h.from_ext != user.number
-		) {
-			continue;
-		} else if (
-			(typeof sent == 'undefined' || !sent) &&
-			h.to_ext != user.number
-		) {
-			continue;
-		}
-		headers.push(h);
-	}
-	msgBase.close();
-	if (typeof ascending === 'undefined' || !ascending) headers.reverse();
-	return headers;
+    if (typeof sent !== 'undefined' &&
+        sent &&
+        user.security.restrictions&UFLAG_K
+    ) {
+        return []; // They'll just see nothing.  Provide actual feedback?  Does anyone use REST K?
+    }
+    var headers = [];
+    var msgBase = new MsgBase('mail');
+    if (!msgBase.open()) return headers;
+    for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
+        var h = msgBase.get_msg_header(m);
+        if (h === null || h.attr&MSG_DELETE) continue;
+        if (    (typeof sent != 'undefined' && sent) &&
+                h.from_ext != user.number
+        ) {
+            continue;
+        } else if (
+            (typeof sent == 'undefined' || !sent) &&
+            h.to_ext != user.number
+        ) {
+            continue;
+        }
+        headers.push(h);
+    }
+    msgBase.close();
+    if (typeof ascending === 'undefined' || !ascending) headers.reverse();
+    return headers;
 }
 
 function mimeDecode (header, body, code) {
-	var ret = {
-		'type' : "",
-		'body' : [],
-		'inlines' : [],
-		'attachments' : []
-	};
-	var msg = mime_decode(header, body, code);
-	if (typeof msg.inlines !== 'undefined') {
-		msg.inlines.forEach(
-			function (inline) {
-				ret.inlines.push(
-					format(
-						'<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;cid=%s" target="_blank">%s</a>',
-						code, header.number, inline, inline
-					)
-				);
-			}
-		);
-	}
-	if (typeof msg.attachments !== 'undefined') {
-		msg.attachments.forEach(
-			function (attachment) {
-				ret.attachments.push(
-					format(
-						'<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;filename=%s" target="_blank">%s</a>',
-						code, header.number, attachment, attachment
-					)
-				);
-			}
-		);
-	}
-	ret.type = msg.type;
-	ret.body = msg.body;
-	return ret;
+    var ret = {
+        'type' : "",
+        'body' : [],
+        'inlines' : [],
+        'attachments' : []
+    };
+    var msg = mime_decode(header, body, code);
+    if (typeof msg.inlines !== 'undefined') {
+        msg.inlines.forEach(
+            function (inline) {
+                ret.inlines.push(
+                    format(
+                        '<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;cid=%s" target="_blank">%s</a>',
+                        code, header.number, inline, inline
+                    )
+                );
+            }
+        );
+    }
+    if (typeof msg.attachments !== 'undefined') {
+        msg.attachments.forEach(
+            function (attachment) {
+                ret.attachments.push(
+                    format(
+                        '<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;filename=%s" target="_blank">%s</a>',
+                        code, header.number, attachment, attachment
+                    )
+                );
+            }
+        );
+    }
+    ret.type = msg.type;
+    ret.body = msg.body;
+    return ret;
 }
 
 function getMailBody (number) {
 
-	var ret = {
-		'type' : '',
-		'body' : '',
-		'inlines' : [],
-		'attachments' : []
-	};
-
-	number = Number(number);
-	if (isNaN(number) || number < 0) return ret;
-
-	var msgBase = new MsgBase('mail');
-	if (!msgBase.open()) return ret;
-	var header = msgBase.get_msg_header(number);
-	if (header !== null	&&
-		(	header.to_ext === user.number ||
-			header.from_ext === user.number
-		)
-	) {
-		var body = msgBase.get_msg_body(false, number, header);
-		if (header.to_ext === user.number && (header.attr^MSG_READ)) {
-			header.attr|=MSG_READ;
-			msgBase.put_msg_header(false, number, header);
-		}
-	}
-	msgBase.close();
-	if (typeof body === 'undefined' || body === null) return ret;
-
-	var decoded = mimeDecode(header, body, 'mail');
-	ret.type = decoded.type;
-	ret.body = formatMessage(decoded.body);
-	ret.inlines = decoded.inlines;
-	ret.attachments = decoded.attachments;
-
-	return ret;
+    var ret = {
+        'type' : '',
+        'body' : '',
+        'inlines' : [],
+        'attachments' : []
+    };
+
+    number = Number(number);
+    if (isNaN(number) || number < 0) return ret;
+
+    var msgBase = new MsgBase('mail');
+    if (!msgBase.open()) return ret;
+    var header = msgBase.get_msg_header(number);
+    if (header !== null &&
+        (   header.to_ext === user.number ||
+            header.from_ext === user.number
+        )
+    ) {
+        var body = msgBase.get_msg_body(false, number, header);
+        if (header.to_ext === user.number && (header.attr^MSG_READ)) {
+            header.attr|=MSG_READ;
+            msgBase.put_msg_header(false, number, header);
+        }
+    }
+    msgBase.close();
+    if (typeof body === 'undefined' || body === null) return ret;
+
+    var decoded = mimeDecode(header, body, 'mail');
+    ret.type = decoded.type;
+    ret.body = formatMessage(decoded.body);
+    ret.inlines = decoded.inlines;
+    ret.attachments = decoded.attachments;
+
+    return ret;
 }
 
 // Returns the user's signature, or an empty String
 function getSignature () {
-	var fn = format('%s/user/%04d.sig', system.data_dir, user.number);
-	if (!file_exists(fn)) return "";
-	var f = new File(fn);
-	f.open('r');
-	var signature = f.read();
-	f.close();
-	return signature;
+    var fn = format('%s/user/%04d.sig', system.data_dir, user.number);
+    if (!file_exists(fn)) return "";
+    var f = new File(fn);
+    f.open('r');
+    var signature = f.read();
+    f.close();
+    return signature;
 }
 
 // Post a messge to 'sub'
 // Called by postNew/postReply, not directly
 function postMessage (sub, header, body) {
-	var ret = false;
-	if (user.alias === settings.guest ||
-		typeof msg_area.sub[sub] === 'undefined' ||
-		!msg_area.sub[sub].can_post	||
-		typeof header.to !== 'string' ||
-		header.to === '' ||
-		typeof header.from !== 'string'	||
-		typeof header.subject !== 'string' ||
+    var ret = false;
+    if (user.alias === settings.guest ||
+        typeof msg_area.sub[sub] === 'undefined' ||
+        !msg_area.sub[sub].can_post ||
+        typeof header.to !== 'string' ||
+        header.to === '' ||
+        typeof header.from !== 'string' ||
+        typeof header.subject !== 'string' ||
 // This could be a reply to a message with no subject, apparently
-//		header.subject == '' ||
-		typeof body !== 'string' ||
-		body === ''
-	) {
-		return ret;
-	}
-	try {
-		var msgBase = new MsgBase(sub);
-		msgBase.open();
-		ret = msgBase.save_msg(header, word_wrap(body));
-		msgBase.close();
-	} catch (err) {
-		log(err);
-	}
-	return ret;
+//      header.subject == '' ||
+        typeof body !== 'string' ||
+        body === ''
+    ) {
+        return ret;
+    }
+    try {
+        var msgBase = new MsgBase(sub);
+        msgBase.open();
+        ret = msgBase.save_msg(header, word_wrap(body));
+        msgBase.close();
+    } catch (err) {
+        log(err);
+    }
+    return ret;
 }
 
 // Post a new (non-reply) message to 'sub'
 function postNew (sub, to, subject, body) {
-	if (typeof sub !== 'string' ||
-		typeof to !== 'string' ||
-		to === '' ||
-		typeof subject !== 'string' ||
-		subject === '' ||
-		typeof body !== 'string' ||
-		body === ''
-	) {
-		return false;
-	}
-	var header = {
-		'to' : to,
-		'from' : user.alias,
-		'subject' : subject
-	};
-	if (sub === 'mail') {
-		return postMail(header, body);
-	} else {
-		return postMessage(sub, header, body);
-	}
+    if (typeof sub !== 'string' ||
+        typeof to !== 'string' ||
+        to === '' ||
+        typeof subject !== 'string' ||
+        subject === '' ||
+        typeof body !== 'string' ||
+        body === ''
+    ) {
+        return false;
+    }
+    var header = {
+        'to' : to,
+        'from' : user.alias,
+        'subject' : subject
+    };
+    if (sub === 'mail') {
+        return postMail(header, body);
+    } else {
+        return postMessage(sub, header, body);
+    }
 }
 
 // Post a message to the mail sub, if this user can do so
 // Called by postNew/postReply, not directly
 function postMail (header, body) {
-	// Lazy ARS checks; we could check the *type* of email being sent, I guess.
-	if (user.security.restrictions&UFLAG_E || user.security.restrictions&UFLAG_M) {
-		return false;
-	}
-	if (	typeof header.to !== 'string' ||
-			typeof header.subject !== 'string' ||
-			typeof body !== 'string'
-	) {
-		return false;
-	}
-	var ret = false;
-	if (user.number < 1 || user.alias === settings.guest) return ret;
-	var na = netaddr_type(header.to);
-	if (na > 0) {
-		header.to_net_type = na;
-		header.to_net_addr = header.to;
-	}
-	var msgBase = new MsgBase('mail');
-	if (msgBase.open()) {
-		ret = msgBase.save_msg(header, word_wrap(body));
-		msgBase.close();
-	}
-	return ret;
+    // Lazy ARS checks; we could check the *type* of email being sent, I guess.
+    if (user.security.restrictions&UFLAG_E || user.security.restrictions&UFLAG_M) {
+        return false;
+    }
+    if (    typeof header.to !== 'string' ||
+            typeof header.subject !== 'string' ||
+            typeof body !== 'string'
+    ) {
+        return false;
+    }
+    var ret = false;
+    if (user.number < 1 || user.alias === settings.guest) return ret;
+    var na = netaddr_type(header.to);
+    if (na > 0) {
+        header.to_net_type = na;
+        header.to_net_addr = header.to;
+    }
+    var msgBase = new MsgBase('mail');
+    if (msgBase.open()) {
+        ret = msgBase.save_msg(header, word_wrap(body));
+        msgBase.close();
+    }
+    return ret;
 }
 
 // Add a new message to 'sub' in reply to parent message 'pid'
 function postReply (sub, body, pid) {
-	var ret = false;
-	if (	typeof sub !== 'string' ||
-			typeof body !== 'string' ||
-			typeof pid !== 'number'
-	) {
-		return ret;
-	}
-	try {
-		var msgBase = new MsgBase(sub);
-		msgBase.open();
-		var pHeader = msgBase.get_msg_header(pid);
-		msgBase.close();
-		if (pHeader === null) return ret;
-		var header = {
-			'to' : pHeader.from,
-			'to_net_addr' : pHeader.from_net_addr,
-			'from' : user.alias,
-			'subject' : pHeader.subject,
-			'thread_back' : pHeader.number
-		};
-		if (sub === 'mail') {
-			ret = postMail(header, body);
-		} else {
-			ret = postMessage(sub, header, body);
-		}
-	} catch (err) {
-		log(err);
-	}
-	return ret;
+    var ret = false;
+    if (    typeof sub !== 'string' ||
+            typeof body !== 'string' ||
+            typeof pid !== 'number'
+    ) {
+        return ret;
+    }
+    try {
+        var msgBase = new MsgBase(sub);
+        msgBase.open();
+        var pHeader = msgBase.get_msg_header(pid);
+        msgBase.close();
+        if (pHeader === null) return ret;
+        var header = {
+            'to' : pHeader.from,
+            'to_net_addr' : pHeader.from_net_addr,
+            'from' : user.alias,
+            'subject' : pHeader.subject,
+            'thread_back' : pHeader.number
+        };
+        if (sub === 'mail') {
+            ret = postMail(header, body);
+        } else {
+            ret = postMessage(sub, header, body);
+        }
+    } catch (err) {
+        log(err);
+    }
+    return ret;
 }
 
 // Delete a message if
 // - This is the mail sub, and the message was sent by or to this user
 // - This is another sub on which the user is an operator
 function deleteMessage (sub, number) {
-	number = parseInt(number);
-	if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail')	{
-		return false;
-	}
-	var msgBase = new MsgBase(sub);
-	if (!msgBase.open()) return false;
-	var header = msgBase.get_msg_header(number);
-	if (header === null) return false;
-	if (	sub === 'mail' &&
-			(header.to_ext === user.number || header.from_ext === user.number)
-	) {
-		var ret = msgBase.remove_msg(number);
-	} else if (sub !== 'mail' && msg_area.sub[sub].is_operator) {
-		var ret = msgBase.remove_msg(number);
-	} else {
-		var ret = false;
-	}
-	msgBase.close();
-	return ret;
+    number = parseInt(number);
+    if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail') {
+        return false;
+    }
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return false;
+    var header = msgBase.get_msg_header(number);
+    if (header === null) return false;
+    if (sub === 'mail' &&
+        (header.to_ext === user.number || header.from_ext === user.number)
+    ) {
+        var ret = msgBase.remove_msg(number);
+    } else if (sub !== 'mail' && msg_area.sub[sub].is_operator) {
+        var ret = msgBase.remove_msg(number);
+    } else {
+        var ret = false;
+    }
+    msgBase.close();
+    return ret;
 }
 
 // Deuce's URL-ifier
 function linkify (body) {
-	urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
-	body = body.replace(
-		urlRE, 
-		function (str) {
-			var ret=''
-			var p=0;
-			var link=str.replace(/\.*$/, '');
-			var linktext=link;
-			if (link.indexOf('://') === -1) link = 'mailto:' + link;
-			return ('<a class="ulLink" href="' + link + '">' + linktext + '</a>' + str.substr(linktext.length));
-		}
-	);
-	return (body);
+    urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
+    body = body.replace(
+        urlRE, 
+        function (str) {
+            var ret=''
+            var p=0;
+            var link=str.replace(/\.*$/, '');
+            var linktext=link;
+            if (link.indexOf('://') === -1) link = 'mailto:' + link;
+            return ('<a class="ulLink" href="' + link + '">' + linktext + '</a>' + str.substr(linktext.length));
+        }
+    );
+    return (body);
 }
 
 // Somewhat modified version of Deuce's "magical quoting stuff" from v3
 function quotify (body) {
 
-	var blockquote_start = '<blockquote>';
-	var blockquote_end = '</blockquote>';
-
-	var lines = body.split(/\r?\n/);
-	body = '';
-
-	var quote_depth=0;
-	var prefixes = [];
-
-	for (l in lines) {
-
-		var line_prefix = '';
-		var m = lines[l].match(/^((?:\s?[^\s]{0,3}&gt;\s?)+)/);
-
-		if (m !== null) {
-
-			var new_prefixes = m[1].match(/\s?[^\s]{0,3}&gt;\s?/g);
-			var p;
-			var broken = false;
-
-			line = lines[l];
-			
-			// If the new length is smaller than the old one, close the extras
-			for (p = new_prefixes.length; p < prefixes.length; p++) {
-				if (quote_depth < 1) continue;
-				line_prefix = line_prefix + blockquote_end;
-				quote_depth--;
-			}
-
-			for (p in new_prefixes) {
-				// Remove prefix from start of line
-				line = line.substr(new_prefixes[p].length);
-
-				if (typeof prefixes[p] === "undefined") {
-					/* New depth */
-					line_prefix = line_prefix + blockquote_start;
-					quote_depth++;
-				} else if (broken) {
-					line_prefix = line_prefix + blockquote_start;
-					quote_depth++;
-				} else if (prefixes[p].replace(/^\s*(.*?)\s*$/,"$1") != new_prefixes[p].replace(/^\s*(.*?)\s*$/,"$1")) {
-					// Close all remaining old prefixes and start one new one
-					var o;
-					for (o = p; o < prefixes.length && o < new_prefixes.length; o++) {
-						if (quote_depth > 0) {
-							line_prefix = blockquote_end + line_prefix;
-							quote_depth--;
-						}
-					}
-					line_prefix = blockquote_start + line_prefix;
-					quote_depth++;
-					broken = true;
-				}
-			}
-
-			prefixes = new_prefixes.slice();
-			line = line_prefix + line;
-
-		} else {
-
-			for (p = 0; p < prefixes.length; p++) {
-				if (quote_depth < 1) continue;
-				line_prefix = line_prefix + blockquote_end;
-				quote_depth--;
-			}
-			prefixes = [];
-			line = line_prefix + lines[l];
-
-		}
-
-		body = body + line + "\r\n";
-
-	}
-
-	if (quote_depth !== 0) {
-		for (;quote_depth > 0; quote_depth--) {
-			body += blockquote_end;
-		}
-	}
-
-	return body.replace(/\<\/blockquote\>\r\n<blockquote\>/g, "\r\n");
+    var blockquote_start = '<blockquote>';
+    var blockquote_end = '</blockquote>';
+
+    var lines = body.split(/\r?\n/);
+    body = '';
+
+    var quote_depth=0;
+    var prefixes = [];
+
+    for (l in lines) {
+
+        var line_prefix = '';
+        var m = lines[l].match(/^((?:\s?[^\s]{0,3}&gt;\s?)+)/);
+
+        if (m !== null) {
+
+            var new_prefixes = m[1].match(/\s?[^\s]{0,3}&gt;\s?/g);
+            var p;
+            var broken = false;
+
+            line = lines[l];
+            
+            // If the new length is smaller than the old one, close the extras
+            for (p = new_prefixes.length; p < prefixes.length; p++) {
+                if (quote_depth < 1) continue;
+                line_prefix = line_prefix + blockquote_end;
+                quote_depth--;
+            }
+
+            for (p in new_prefixes) {
+                // Remove prefix from start of line
+                line = line.substr(new_prefixes[p].length);
+
+                if (typeof prefixes[p] === "undefined") {
+                    /* New depth */
+                    line_prefix = line_prefix + blockquote_start;
+                    quote_depth++;
+                } else if (broken) {
+                    line_prefix = line_prefix + blockquote_start;
+                    quote_depth++;
+                } else if (prefixes[p].replace(/^\s*(.*?)\s*$/,"$1") != new_prefixes[p].replace(/^\s*(.*?)\s*$/,"$1")) {
+                    // Close all remaining old prefixes and start one new one
+                    var o;
+                    for (o = p; o < prefixes.length && o < new_prefixes.length; o++) {
+                        if (quote_depth > 0) {
+                            line_prefix = blockquote_end + line_prefix;
+                            quote_depth--;
+                        }
+                    }
+                    line_prefix = blockquote_start + line_prefix;
+                    quote_depth++;
+                    broken = true;
+                }
+            }
+
+            prefixes = new_prefixes.slice();
+            line = line_prefix + line;
+
+        } else {
+
+            for (p = 0; p < prefixes.length; p++) {
+                if (quote_depth < 1) continue;
+                line_prefix = line_prefix + blockquote_end;
+                quote_depth--;
+            }
+            prefixes = [];
+            line = line_prefix + lines[l];
+
+        }
+
+        body = body + line + "\r\n";
+
+    }
+
+    if (quote_depth !== 0) {
+        for (;quote_depth > 0; quote_depth--) {
+            body += blockquote_end;
+        }
+    }
+
+    return body.replace(/\<\/blockquote\>\r\n<blockquote\>/g, "\r\n");
 
 }
 
 // Format message body for the web
 function formatMessage (body, ansi) {
 
-	// Workaround for html_encode(body, true, false, false, false);
-	// which causes a crash if body is empty
-	if (body === '') return body;
-
-	if (typeof ansi === 'boolean' && ansi) {
-
-		body = html_encode(body, true, false, true, true);
-		body = body.replace(/\r?\n+(<\/span>)?$/,'$1');
-		body = linkify(body);
-
-		// Get the last line
-		var body_m = body.match(/\n([^\n]*)$/);
-		if (body_m !== null) {
-			body = '<pre>'+body;
-			body_m[1] = body_m[1].replace(/&[^;]*;/g,".");
-			body_m[1] = body_m[1].replace(/<[^>]*>/g,"");
-			var lenremain = 80 - body_m[1].length;
-			while (lenremain > 0) {
-				body += '&nbsp;';
-				lenremain--;
-			}
-			body += '</pre>';
-		} else {
-			/* If we couldn't get the last line, add a line of 80 columns */
-			var line = "";
-			for (n = 0; n < 80; n++) {
-				line += '&nbsp;';
-			}
-			body = '<pre>' + body + line + "</pre>";
-		}
-
-	} else {
-
-		// Strip CTRL-A
-		body = body.replace(/\1./g,'');
-		// Strip ANSI
-		body = body.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g,'');
-		body = body.replace(/\x1b[\x40-\x7e]/g,'');
-		// Strip unprintable control chars (NULL, BEL, DEL, ESC)
-		body = body.replace(/[\x00\x07\x1b\x7f]/g,'');
-
-		// Format for the web
-		body = word_wrap(body, body.length);
-		body = html_encode(body, true, false, false, false);
-		body = quotify(body);
-		body = linkify(body);
-		body = body.replace(/\r\n$/,'');
-		body = body.replace(/(\r?\n)/g, "<br>$1");
-
-	}
-
-	return body;
+    // Workaround for html_encode(body, true, false, false, false);
+    // which causes a crash if body is empty
+    if (body === '') return body;
+
+    if (typeof ansi === 'boolean' && ansi) {
+
+        body = html_encode(body, true, false, true, true);
+        body = body.replace(/\r?\n+(<\/span>)?$/,'$1');
+        body = linkify(body);
+
+        // Get the last line
+        var body_m = body.match(/\n([^\n]*)$/);
+        if (body_m !== null) {
+            body = '<pre>'+body;
+            body_m[1] = body_m[1].replace(/&[^;]*;/g,".");
+            body_m[1] = body_m[1].replace(/<[^>]*>/g,"");
+            var lenremain = 80 - body_m[1].length;
+            while (lenremain > 0) {
+                body += '&nbsp;';
+                lenremain--;
+            }
+            body += '</pre>';
+        } else {
+            /* If we couldn't get the last line, add a line of 80 columns */
+            var line = "";
+            for (n = 0; n < 80; n++) {
+                line += '&nbsp;';
+            }
+            body = '<pre>' + body + line + "</pre>";
+        }
+
+    } else {
+
+        // Strip CTRL-A
+        body = body.replace(/\1./g,'');
+        // Strip ANSI
+        body = body.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g,'');
+        body = body.replace(/\x1b[\x40-\x7e]/g,'');
+        // Strip unprintable control chars (NULL, BEL, DEL, ESC)
+        body = body.replace(/[\x00\x07\x1b\x7f]/g,'');
+
+        // Format for the web
+        body = word_wrap(body, body.length);
+        body = html_encode(body, true, false, false, false);
+        body = quotify(body);
+        body = linkify(body);
+        body = body.replace(/\r\n$/,'');
+        body = body.replace(/(\r?\n)/g, "<br>$1");
+
+    }
+
+    return body;
 
 }
 
 function setScanCfg (sub, cfg) {
 
-	var opts = [
-		0,
-		SCAN_CFG_NEW,
-		SCAN_CFG_YONLY
-	];
+    var opts = [
+        0,
+        SCAN_CFG_NEW,
+        SCAN_CFG_YONLY
+    ];
 
-	if (typeof msg_area.sub[sub] === 'undefined') return false;
+    if (typeof msg_area.sub[sub] === 'undefined') return false;
 
-	cfg = parseInt(cfg);
-	if (isNaN(cfg) || cfg < 0 || cfg > 2) return false;
+    cfg = parseInt(cfg);
+    if (isNaN(cfg) || cfg < 0 || cfg > 2) return false;
 
-	if (cfg === 2) opts[cfg]|=SCAN_CFG_NEW;
+    if (cfg === 2) opts[cfg]|=SCAN_CFG_NEW;
 
-	msg_area.sub[sub].scan_cfg = opts[cfg];
-	return true;
+    msg_area.sub[sub].scan_cfg = opts[cfg];
+    return true;
 
 }
\ No newline at end of file
-- 
GitLab


From 498ed8be06743ebfcd67ec6d7550ce84e01446ca Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Nov 2015 17:58:49 -0500
Subject: [PATCH 009/752] Foreward? :|

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 33f75af5c0..6d46bedf0c 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ Port=1513
 Options=NO_HOST_LOOKUP
 Command=websocket-rlogin-service.js
 ```
-- Tell your router and firewall to open and foreward ports *1123* and *1513* to your BBS
+- Tell your router and firewall to open and forward ports *1123* and *1513* to your BBS
 
 ###Configuration
 
-- 
GitLab


From b2693604170df7566bfbee17f73976510ca8b1e7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Nov 2015 01:21:41 -0500
Subject: [PATCH 010/752] Include user number (or 0) in response.

---
 web/root/api/auth.ssjs | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 33f9899e27..0c6495b6c0 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -1,11 +1,13 @@
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "auth.js");
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'auth.js');
 
 var response = JSON.stringify(
-	{ 'authenticated' : (user.alias != settings.guest) }
+	{	'authenticated' : (user.alias !== settings.guest),
+		'user' : (user.alias === settings.guest ? 0 : user.number)
+	}
 );
 
-http_reply.header["Content-Type"] = "application/json";
-http_reply.header["Content-Length"] = response.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = response.length;
 
 write(response);
\ No newline at end of file
-- 
GitLab


From 152bb7892415563ba010496a6c16739a9d2ea779 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Nov 2015 01:44:01 -0500
Subject: [PATCH 011/752] 'head'

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 6d46bedf0c..f44c9f958d 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ Command=websocket-rlogin-service.js
 
 - This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
 	- You can place your own CSS overrides in *web/root/css/style.css*
-	- You can load another stylesheet of your own choosing in the <head> section of *web/root/index.xjs* (load it after the others)
+	- You can load another stylesheet of your own choosing in the &lt;head&gt; section of *web/root/index.xjs* (load it after the others)
 - The sidebar module & page structure is *mostly* similar to the system used in ecWeb v3.  See [the old instructions](http://wiki.synchro.net/howto:ecweb#the_sidebar) for info on adding content.
 	- You can force a link to a page to be placed in the *More* menu by using an underscore as a separator in its filename rather than a hyphen
 
-- 
GitLab


From 6b3ad8fd9b08a0118e2b742069ed272d6130dc74 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Nov 2015 00:38:52 -0500
Subject: [PATCH 012/752] listGroups doesn't need to return the entire sub_list
 array for each group. :|

---
 web/lib/forum.js | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index e67ceb07aa..878706bc20 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -6,7 +6,15 @@ function listGroups () {
     var response = [];
     msg_area.grp_list.forEach(
         function (grp) {
-            if (grp.sub_list.length > 0) response.push(grp);
+            if (grp.sub_list.length < 1)
+                return;
+            response.push(
+                {   'index' : grp.index,
+                    'name' : grp.name,
+                    'description' : grp.description,
+                    'sub_count' : grp.sub_list.length
+                }
+            );
         }
     );
     return response;
-- 
GitLab


From 40c527b09015c03315252302f6c30c5e6027bfc3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Nov 2015 00:39:45 -0500
Subject: [PATCH 013/752] listGroups return value now just has sub_count
 property instead of the sub_list array.

---
 web/root/pages/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 6e9b18aa1a..e037341835 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -342,7 +342,7 @@ if(	typeof http_request.query.sub != "undefined"
 				group.index,
 				group.index,
 				group.description,
-				group.sub_list.length
+				group.sub_count
 			)
 		);
 	}
-- 
GitLab


From 18fc185b0f82e7ceaa38d7d2c8fdbcddde25d184 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Nov 2015 01:35:26 -0500
Subject: [PATCH 014/752] Added get-thread-unread-count call.

---
 web/root/api/forum.ssjs | 270 ++++++++++++++++++++--------------------
 1 file changed, 138 insertions(+), 132 deletions(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 8d8ea32e09..d763310105 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -1,146 +1,152 @@
-/*	This script is an interface between HTTP clients and the functions defined
-	in web/lib/forum.js.  A basic check for an authenticated, non-guest user
-	is done here; otherwise all permission checking is done at the function
-	level. */
+/*  This script is an interface between HTTP clients and the functions defined
+    in web/lib/forum.js.  A basic check for an authenticated, non-guest user
+    is done here; otherwise all permission checking is done at the function
+    level. */
 
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "auth.js");
-load(settings.web_lib + "forum.js");
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'forum.js');
 
 var reply = {};
 
 // There must be an API call, and the user must not be a guest or unknown
-if(	(http_request.method == "GET" || http_request.method == "POST")
-	&&
-	typeof http_request.query.call != "undefined"
-	&&
-	user.number > 0
-	&&
-	user.alias != settings.guest
+if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+    typeof http_request.query.call !== 'undefined' &&
+    user.number > 0 &&
+    user.alias !== settings.guest
 ) {
 
-	switch(http_request.query.call[0].toLowerCase()) {
+    switch(http_request.query.call[0].toLowerCase()) {
 
-		case "list-groups":
-			reply = listGroups();
-			break;
-		
-		case "list-subs":
-			if(typeof http_request.query.group != "undefined")
-				reply = listSubs(http_request.query.group[0]);
-			break;
-		
-		case "list-threads":
-			if(typeof http_request.query.sub != "undefined")
-				reply = listThreads(http_request.query.sub[0]);
-			break;
-		
-		case "get-sub-unread-count":
-			if(typeof http_request.query.sub != "undefined") {
-				http_request.query.sub.forEach(
-					function(sub) {
-						reply[sub] = getSubUnreadCount(sub);
-					}
-				);
-			}
-			break;
-		
-		case "get-group-unread-count":
-			if(typeof http_request.query.group != "undefined") {
-				http_request.query.group.forEach(
-					function(group) {
-						reply[group] = getGroupUnreadCount(group);
-					}
-				);
-			}
-			break;
-		
-		case "get-mail-unread-count":
-			reply.count = getMailUnreadCount();
-			break;
-		
-		case "get-mail-body":
-			if(typeof http_request.query.number != "undefined")
-				reply = getMailBody(http_request.query.number[0]);
-			break;
-		
-		case "get-signature":
-			reply.signature = getSignature();
-			break;
-		
-		case "post-reply":
-			if(	typeof http_request.query.sub != "undefined"
-				&&
-				typeof http_request.query.body != "undefined"
-				&&
-				typeof http_request.query.pid != "undefined"
-			) {
-				reply.success = postReply(
-					http_request.query.sub[0],
-					http_request.query.body[0],
-					Number(http_request.query.pid[0])
-				);
-			} else {
-				reply.success = false;
-			}
-			break;
-		
-		case "post":
-			if( typeof http_request.query.sub != "undefined"
-				&&
-				typeof http_request.query.to != "undefined"
-				&&
-				typeof http_request.query.subject != "undefined"
-				&&
-				typeof http_request.query.body != "undefined"
-			) {
-				reply.success = postNew(
-					http_request.query.sub[0],
-					http_request.query.to[0],
-					http_request.query.subject[0],
-					http_request.query.body[0]
-				);
-			} else {
-				reply.success = false;
-			}
-			break;
-		
-		case "delete-message":
-			if( typeof http_request.query.sub != "undefined"
-				&&
-				typeof http_request.query.number != "undefined"
-			) {
-				reply.success = deleteMessage(
-					http_request.query.sub[0],
-					http_request.query.number[0]
-				);
-			} else {
-				reply.success = false;
-			}
-			break;
+        case 'list-groups':
+            reply = listGroups();
+            break;
+        
+        case 'list-subs':
+            if (typeof http_request.query.group !== 'undefined') {
+                reply = listSubs(http_request.query.group[0]);
+            }
+            break;
+        
+        case 'list-threads':
+            if (typeof http_request.query.sub !== 'undefined') {
+                reply = listThreads(http_request.query.sub[0]);
+            }
+            break;
+        
+        case 'get-group-unread-count':
+            if (typeof http_request.query.group !== 'undefined') {
+                http_request.query.group.forEach(
+                    function(group) {
+                        reply[group] = getGroupUnreadCount(group);
+                    }
+                );
+            }
+            break;
 
-		case "set-scan-cfg":
-			if(	typeof http_request.query.sub != "undefined"
-				&&
-				typeof http_request.query.cfg != "undefined"
-			) {
-				reply.success = setScanCfg(
-					http_request.query.sub[0],
-					http_request.query.cfg[0]
-				);
-			} else {
-				reply.success = false;
-			}
-			break;
-		
-		default:
-			break;
-			
-	}
+        case 'get-sub-unread-count':
+            if (typeof http_request.query.sub !== 'undefined') {
+                http_request.query.sub.forEach(
+                    function(sub) {
+                        reply[sub] = getSubUnreadCount(sub);
+                    }
+                );
+            }
+            break;
+
+        case 'get-thread-unread-count':
+            if (typeof http_request.query.sub !== 'undefined' &&
+                typeof http_request.query.thread !== 'undefined'
+            ) {
+                reply.sub = http_request.query.sub[0];
+                reply.thread = http_request.query.thread[0];
+                reply.unread = getUnreadInThread(
+                    http_request.query.sub[0],
+                    http_request.query.thread[0]
+                );
+            }
+            break;
+        
+        case 'get-mail-unread-count':
+            reply.count = getMailUnreadCount();
+            break;
+        
+        case 'get-mail-body':
+            if (typeof http_request.query.number !== 'undefined') {
+                reply = getMailBody(http_request.query.number[0]);
+            }
+            break;
+        
+        case 'get-signature':
+            reply.signature = getSignature();
+            break;
+        
+        case 'post-reply':
+            if (typeof http_request.query.sub !== 'undefined' &&
+                typeof http_request.query.body !== 'undefined' &&
+                typeof http_request.query.pid !== 'undefined'
+            ) {
+                reply.success = postReply(
+                    http_request.query.sub[0],
+                    http_request.query.body[0],
+                    Number(http_request.query.pid[0])
+                );
+            } else {
+                reply.success = false;
+            }
+            break;
+        
+        case 'post':
+            if (typeof http_request.query.sub !== 'undefined' &&
+                typeof http_request.query.to !== 'undefined' &&
+                typeof http_request.query.subject !== 'undefined' &&
+                typeof http_request.query.body !== 'undefined'
+            ) {
+                reply.success = postNew(
+                    http_request.query.sub[0],
+                    http_request.query.to[0],
+                    http_request.query.subject[0],
+                    http_request.query.body[0]
+                );
+            } else {
+                reply.success = false;
+            }
+            break;
+        
+        case 'delete-message':
+            if (typeof http_request.query.sub !== 'undefined' &&
+                typeof http_request.query.number !== 'undefined'
+            ) {
+                reply.success = deleteMessage(
+                    http_request.query.sub[0],
+                    http_request.query.number[0]
+                );
+            } else {
+                reply.success = false;
+            }
+            break;
+
+        case 'set-scan-cfg':
+            if (typeof http_request.query.sub !== 'undefined' &&
+                typeof http_request.query.cfg !== 'undefined'
+            ) {
+                reply.success = setScanCfg(
+                    http_request.query.sub[0],
+                    http_request.query.cfg[0]
+                );
+            } else {
+                reply.success = false;
+            }
+            break;
+        
+        default:
+            break;
+            
+    }
 
 }
 
 reply = JSON.stringify(reply);
-http_reply.header["Content-Type"] = "application/json";
-http_reply.header["Content-Length"] = reply.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
 write(reply);
\ No newline at end of file
-- 
GitLab


From cb88e3bc55ea9c5227f46ba84b690b0de30ff2d8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Nov 2015 01:51:56 -0500
Subject: [PATCH 015/752] getUnreadInThread by thread object or thread ID.

---
 web/lib/forum.js | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 878706bc20..f02594065e 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,4 +1,5 @@
 load('sbbsdefs.js');
+load('msgutils.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
 
@@ -105,6 +106,12 @@ function getGroupUnreadCount (group) {
 }
 
 function getUnreadInThread (sub, thread) {
+    if (typeof thread === 'number') {
+        var threads = getMessageThreads(sub);
+        if(typeof threads.thread[thread] === 'undefined')
+            return 0;
+        thread = threads.thread[thread];
+    }
     var count = 0;
     thread.messages.forEach(
         function (header) {
-- 
GitLab


From 644256cca26ce942223123b9a78d133504a24baf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Nov 2015 02:11:00 -0500
Subject: [PATCH 016/752] On second thought, include total and unread counts
 along with first message header in the listThreads(sub) function that's
 supposed to be here.

---
 web/lib/forum.js | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index f02594065e..f01c2b7d36 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -55,6 +55,29 @@ function listSubs (group) {
     return response;
 }
 
+function listThreads (sub) {
+    var response = [];
+    if (typeof msg_area.sub[sub] === 'undefined') return response;
+    try {
+        var threads = getMessageThreads(sub);
+        Object.keys(threads.thread).forEach(
+            function(thread) {
+                if (threads.thread[thread].messages.length < 1)
+                    return;
+                var ret = {
+                    header : threads.thread[thread].messages[0],
+                    messages : threads.thread[thread].messages.length,
+                    unread : getUnreadInThread(sub, threads.thread[thread])
+                };
+                response.push(ret);
+            }
+        )
+    } catch (err) {
+        log(err);
+    }
+    return response;
+}
+
 function getSubUnreadCount (sub) {
     var ret = {
         'scanned' : 0,
@@ -106,12 +129,6 @@ function getGroupUnreadCount (group) {
 }
 
 function getUnreadInThread (sub, thread) {
-    if (typeof thread === 'number') {
-        var threads = getMessageThreads(sub);
-        if(typeof threads.thread[thread] === 'undefined')
-            return 0;
-        thread = threads.thread[thread];
-    }
     var count = 0;
     thread.messages.forEach(
         function (header) {
-- 
GitLab


From 9d87a9809a7820e420b83af56f51096d2bd15736 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Nov 2015 02:13:00 -0500
Subject: [PATCH 017/752] The list-threads call wasn't actually backed by a
 function in ../../lib/forum.js.  It is now, and includes per-thread unread
 message counts.  Removed the get-unread-in-thread call.

---
 web/root/api/forum.ssjs | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index d763310105..7a3f1b005c 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -54,19 +54,6 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
             }
             break;
 
-        case 'get-thread-unread-count':
-            if (typeof http_request.query.sub !== 'undefined' &&
-                typeof http_request.query.thread !== 'undefined'
-            ) {
-                reply.sub = http_request.query.sub[0];
-                reply.thread = http_request.query.thread[0];
-                reply.unread = getUnreadInThread(
-                    http_request.query.sub[0],
-                    http_request.query.thread[0]
-                );
-            }
-            break;
-        
         case 'get-mail-unread-count':
             reply.count = getMailUnreadCount();
             break;
-- 
GitLab


From a48954b8f55452b665facc06c5ba945cdfa8b8f4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Dec 2015 00:12:21 -0500
Subject: [PATCH 018/752] ecWeb v3 notes.

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index f44c9f958d..9478a261d1 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,8 @@ Options=NO_HOST_LOOKUP
 Command=websocket-rlogin-service.js
 ```
 - Tell your router and firewall to open and forward ports *1123* and *1513* to your BBS
+- If you were running ecWeb v3 and modified the *RootDirectory* value in the *[Web]* section of *ctrl/sbbs.ini* to point to *../web/root/ecwebv3*, change it back to *../web/root*.
+- Start your BBS back up again
 
 ###Configuration
 
-- 
GitLab


From 43030669f135f515d04ee639862149369582d2a7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Dec 2015 15:19:45 -0500
Subject: [PATCH 019/752] Typo re: addReply

---
 web/root/js/forum.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index a176df190c..cc96de88c8 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -90,6 +90,7 @@ var deleteMessage = function(sub, id) {
 	$.getJSON(
 		'./api/forum.ssjs?call=delete-message&sub=' + sub + '&number=' + id,
 		function(data) {
+			console.log(data);
 			if(data.success) {
 				$('#li-' + id).remove();
 				insertParam("notice", "Message deleted.");
@@ -190,7 +191,7 @@ var getMailBody = function(id) {
 					'aria-label="Reply to this message" ' +
 					'title="Reply to this message" ' +
 					'name="reply-' + id + '" ' +
-					'onclick="addReply(\'mail\'),' + id + '">' +
+					'onclick="addReply(\'mail\',' + id + ')">' +
 					'<span class="glyphicon glyphicon-comment"></span>' +
 					'</button>' +
 					'<button class="btn btn-default icon" aria-label="Delete this message" ' +
@@ -223,4 +224,4 @@ var setScanCfg = function(sub, cfg) {
 			);
 		}
 	);
-}
\ No newline at end of file
+}
-- 
GitLab


From a25c14185b46a1d180af6b9f3171a543ce11ddc0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Dec 2015 15:21:05 -0500
Subject: [PATCH 020/752] Apparently the to_ext and from_ext values in a
 message header are strings that represent numbers.  We'll just do a
 loose-typed comparison in 'deleteMessage'.  This may be a problem elsewhere;
 I'll have to see.

---
 web/lib/forum.js | 34 +++++++++-------------------------
 1 file changed, 9 insertions(+), 25 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index f01c2b7d36..6ee8b3bdbf 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -55,29 +55,6 @@ function listSubs (group) {
     return response;
 }
 
-function listThreads (sub) {
-    var response = [];
-    if (typeof msg_area.sub[sub] === 'undefined') return response;
-    try {
-        var threads = getMessageThreads(sub);
-        Object.keys(threads.thread).forEach(
-            function(thread) {
-                if (threads.thread[thread].messages.length < 1)
-                    return;
-                var ret = {
-                    header : threads.thread[thread].messages[0],
-                    messages : threads.thread[thread].messages.length,
-                    unread : getUnreadInThread(sub, threads.thread[thread])
-                };
-                response.push(ret);
-            }
-        )
-    } catch (err) {
-        log(err);
-    }
-    return response;
-}
-
 function getSubUnreadCount (sub) {
     var ret = {
         'scanned' : 0,
@@ -129,6 +106,12 @@ function getGroupUnreadCount (group) {
 }
 
 function getUnreadInThread (sub, thread) {
+    if (typeof thread === 'number') {
+        var threads = getMessageThreads(sub);
+        if(typeof threads.thread[thread] === 'undefined')
+            return 0;
+        thread = threads.thread[thread];
+    }
     var count = 0;
     thread.messages.forEach(
         function (header) {
@@ -395,9 +378,10 @@ function deleteMessage (sub, number) {
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return false;
     var header = msgBase.get_msg_header(number);
+	log(JSON.stringify(header));
     if (header === null) return false;
     if (sub === 'mail' &&
-        (header.to_ext === user.number || header.from_ext === user.number)
+        (header.to_ext == user.number || header.from_ext == user.number)
     ) {
         var ret = msgBase.remove_msg(number);
     } else if (sub !== 'mail' && msg_area.sub[sub].is_operator) {
@@ -589,4 +573,4 @@ function setScanCfg (sub, cfg) {
     msg_area.sub[sub].scan_cfg = opts[cfg];
     return true;
 
-}
\ No newline at end of file
+}
-- 
GitLab


From 4adf2fb1d136e375c4f14cb16efb6f2621442d77 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Dec 2015 15:28:23 -0500
Subject: [PATCH 021/752] More loose typing. :|  Lame.

---
 web/lib/forum.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 6ee8b3bdbf..12ed8d2c20 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -220,12 +220,12 @@ function getMailBody (number) {
     if (!msgBase.open()) return ret;
     var header = msgBase.get_msg_header(number);
     if (header !== null &&
-        (   header.to_ext === user.number ||
-            header.from_ext === user.number
+        (   header.to_ext == user.number ||
+            header.from_ext == user.number
         )
     ) {
         var body = msgBase.get_msg_body(false, number, header);
-        if (header.to_ext === user.number && (header.attr^MSG_READ)) {
+        if (header.to_ext == user.number && (header.attr^MSG_READ)) {
             header.attr|=MSG_READ;
             msgBase.put_msg_header(false, number, header);
         }
-- 
GitLab


From 5a10c4ce6d1b3ef4430b045d8f8ad160bfd48bd7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Dec 2015 15:48:51 -0500
Subject: [PATCH 022/752] Attempt to resolve weird fTelnet splash screen by
 opening file in 'rb' mode.

---
 web/lib/ftelnet.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index cea9a8b7ec..22d926318b 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -7,8 +7,8 @@ f.close();
 
 var getSplash = function() {
 	var f = new File(settings.ftelnet_splash);
-	f.open("r");
+	f.open("rb");
 	var splash = base64_encode(f.read());
 	f.close();
 	return splash;
-}
\ No newline at end of file
+}
-- 
GitLab


From 23ca32c935a844e24bc3c74f15d449d771c16734 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Dec 2015 17:57:38 -0500
Subject: [PATCH 023/752] mods/logon.js instructions

---
 README.md | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 9478a261d1..029bf00b4a 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,19 @@ Command=websocket-rlogin-service.js
 ```
 - Tell your router and firewall to open and forward ports *1123* and *1513* to your BBS
 - If you were running ecWeb v3 and modified the *RootDirectory* value in the *[Web]* section of *ctrl/sbbs.ini* to point to *../web/root/ecwebv3*, change it back to *../web/root*.
+
+- Open your *mods/logon.js* file *or, if it doesn't exist yet* copy your *exec/logon.js* file to *mods/logon.js* and then open it
+- Add the following block of code to the top of the file, under the line that says *load('sbbsdefs.js')*:
+```js
+if (bbs.connection.toUpperCase() == "RLOGIN" &&
+   typeof xtrn_area.prog[console.terminal] != "undefined"
+) {
+        bbs.exec_xtrn(console.terminal);
+        bbs.hangup();
+        exit();
+}
+```
+
 - Start your BBS back up again
 
 ###Configuration
@@ -88,4 +101,4 @@ Command=websocket-rlogin-service.js
 - To stop using this web interface, you can just revert to your previous *web* directory at any time.
 - The [web] section added to *ctrl/modopts.ini* won't hurt anything if you leave it there, but you can delete it if you want
 - Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
-- Undo any changes you made to your firewall & router during the *Quick Start*
\ No newline at end of file
+- Undo any changes you made to your firewall & router during the *Quick Start*
-- 
GitLab


From 20227e386fa492f0d95201d52699c728bbd2012a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Dec 2015 00:15:21 -0500
Subject: [PATCH 024/752] Avoid errors by checking that dir/lib actually exists
 before attempting to list.

---
 web/root/pages/002-files.ssjs | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index 88c0d49124..cbbd45af66 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -1,14 +1,16 @@
 //Files
 
-if(typeof argv[0] != "boolean" || !argv[0])
+if(typeof argv[0] !== 'boolean' || !argv[0])
 	exit();
 
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "files.js");
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'files.js');
 
 //writeln('<script type="text/javascript" src="./js/files.js"></script>');
 
-if(typeof http_request.query.dir != "undefined") {
+if (typeof http_request.query.dir !== 'undefined' &&
+	typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
+) {
 	// File listing
 
 	writeln(
@@ -25,7 +27,7 @@ if(typeof http_request.query.dir != "undefined") {
 		'</ol>'
 	);
 
-	var writeFileDetails = function(file) {
+	function writeFileDetails(file) {
 		writeln(
 			format(
 				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
@@ -50,7 +52,10 @@ if(typeof http_request.query.dir != "undefined") {
 	writeln('</div>');
 
 
-} else if(typeof http_request.query.library != "undefined") {
+} else if(
+	typeof http_request.query.library !== 'undefined' &&
+	typeof file_area.lib_list[http_request.query.library[0]] !== 'undefined'
+) {
 	// File directory listing
 
 	writeln(
@@ -64,7 +69,7 @@ if(typeof http_request.query.dir != "undefined") {
 		'</ol>'
 	);
 
-	var writeDirectory = function(dir) {
+	function writeDirectory(dir) {
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;dir=%s" class="list-group-item striped">' +
@@ -95,7 +100,7 @@ if(typeof http_request.query.dir != "undefined") {
 		'</ol>'
 	);
 
-	var writeLibrary = function(library) {
+	function writeLibrary(library) {
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;library=%s" class="list-group-item striped">' +
-- 
GitLab


From 04a0f54820c2c2bd46146b592569364b73bdff5c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Dec 2015 00:18:32 -0500
Subject: [PATCH 025/752] Attempt to speed up the locating of the desired file.

---
 web/root/api/files.ssjs | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 2b45fc541b..82eea350dc 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -27,15 +27,16 @@ if(	(http_request.method == "GET" || http_request.method == "POST")
 			) {
 				var fileDir = new FileDir(file_area.dir[http_request.query.dir[0]]);
 				var file = null;
-				fileDir.files.forEach(
-					function(f) {
-						if(f.name.toLowerCase() != http_request.query.file[0].toLowerCase())
-							return;
-						file = f;
+				for (var f = 0; f < fileDir.files.length; f++) {
+					if (fileDir.files[f].name.toLowerCase() !==
+						http_request.query.file[0].toLowerCase()
+					) {
+						continue;
 					}
-				);
-				if(file === null)
+					file = fileDir.files[f];
 					break;
+				}
+				if (file === null) break;
 				client.socket.send('HTTP/1.0 Status: 200 OK\r\n');
 				client.socket.send('Content-Type: application/octet-stream\r\n');
 				client.socket.send('Content-Disposition: attachment; filename="' + file.name + '";\r\n');
-- 
GitLab


From 6e71be7ed8295e3aa07603e2a6f3685cc1d64885 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Dec 2015 00:21:31 -0500
Subject: [PATCH 026/752] Cleanup

---
 web/root/api/files.ssjs | 45 +++++++++++++++++------------------------
 1 file changed, 19 insertions(+), 26 deletions(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 82eea350dc..7e3125a215 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,29 +1,23 @@
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "auth.js");
-load(settings.web_lib + "files.js");
-load("filedir.js");
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'files.js');
+load('filedir.js');
 
 var reply = {};
 
-if(	(http_request.method == "GET" || http_request.method == "POST")
-	&&
-	typeof http_request.query.call != "undefined"
-	&&
-	user.number > 0
-	&&
-	user.alias != settings.guest
+if ((http_request.method === "GET" || http_request.method === "POST") &&
+	typeof http_request.query.call !== 'undefined' &&
+	user.number > 0	&&
+	user.alias !== settings.guest
 ) {
 
-	switch(http_request.query.call[0].toLowerCase()) {
-		case "download-file":
+	switch (http_request.query.call[0].toLowerCase()) {
+		case 'download-file':
 			reply = false;
-			if(	typeof http_request.query.dir != "undefined"
-				&&
-				typeof file_area.dir[http_request.query.dir[0]] != "undefined"
-				&&
-				file_area.dir[http_request.query.dir[0]].can_download
-				&&
-				typeof http_request.query.file != "undefined"
+			if(	typeof http_request.query.dir !== 'undefined' &&
+				typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
+				file_area.dir[http_request.query.dir[0]].can_download &&
+				typeof http_request.query.file !== 'undefined'
 			) {
 				var fileDir = new FileDir(file_area.dir[http_request.query.dir[0]]);
 				var file = null;
@@ -44,8 +38,8 @@ if(	(http_request.method == "GET" || http_request.method == "POST")
 				client.socket.send('Content-Length: ' + file_size(file.fullPath) + '\r\n');
 				client.socket.send('\r\n');
 				var f = new File(file.fullPath);
-				f.open("r+b");
-				while(!f.eof) {
+				f.open('r+b');
+				while (!f.eof) {
 					client.socket.sendBin(f.readBin(1), 1);
 				}
 				f.close();
@@ -57,10 +51,9 @@ if(	(http_request.method == "GET" || http_request.method == "POST")
 
 }
 
-if(!reply)
-	exit();
+if (!reply) exit();
 
 reply = JSON.stringify(reply);
-http_reply.header["Content-Type"] = "application/json";
-http_reply.header["Content-Length"] = reply.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
 write(reply);
\ No newline at end of file
-- 
GitLab


From 5832e9bce08ddf9d6127f0a5888be6bc223ee441 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Dec 2015 01:19:41 -0500
Subject: [PATCH 027/752] Cleanup

---
 web/lib/auth.js | 127 +++++++++++++++++++++++-------------------------
 1 file changed, 60 insertions(+), 67 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index def3f73318..bf59ac1fae 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -1,77 +1,75 @@
 load("sbbsdefs.js");
 load(system.exec_dir + "../web/lib/init.js");
 
-var randomString = function(length) {
+function randomString(length) {
 	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split("");
 	var str = '';
-	for (var i = 0; i < length; i++)
+	for (var i = 0; i < length; i++) {
 		str += chars[Math.floor(Math.random() * chars.length)];
+	}
 	return str;
 }
 
-var getSession = function(un) {
-	var fn = format("%suser/%04d.web", system.data_dir, un);
-	if(!file_exists(fn))
-		return false;
+function getSession(un) {
+	var fn = format('%suser/%04d.web', system.data_dir, un);
+	if (!file_exists(fn)) return false;
 	var f = new File(fn);
-	if(!f.open("r"))
-		return false;
+	if (!f.open('r')) return false;
 	var session = f.iniGetObject();
 	f.close();
 	return session;
 }
 
-var getSessionValue = function(un, key) {
+function getSessionValue(un, key) {
 	var session = getSession(un);
-	if(!session || typeof session[key] == "undefined")
-		return null;
+	if (!session || typeof session[key] === 'undefined') return null;
 	return session[key];
 }
 
-var setSessionValue = function(un, key, value) {
-	var fn = format("%suser/%04d.web", system.data_dir, un);
+function setSessionValue(un, key, value) {
+	var fn = format('%suser/%04d.web', system.data_dir, un);
 	var f = new File(fn);
 	f.open(f.exists ? 'r+' : 'w+');
 	f.iniSetValue(null, key, value);
 	f.close();
 }
 
-var setCookie = function(usr, sessionKey) {
-	if(usr instanceof User && usr.number > 0) {
+function setCookie(usr, sessionKey) {
+	if (usr instanceof User && usr.number > 0) {
 		set_cookie(
 			'synchronet',
 			usr.number + ',' + sessionKey,
 			(time() + settings.timeout),
-			http_request.host.replace(/\:\d*/g, ""),
-			"/"
+			http_request.host.replace(/\:\d*/g, ''),
+			'/'
 		);
 		setSessionValue(usr.number, 'key', sessionKey);
 	}
 }
 
-var validateSession = function(cookies) {
+function validateSession(cookies) {
 
-	for(var c in cookies) {
+	for (var c in cookies) {
 
-		if(cookies[c].search(/^\d+,\w+$/) < 0)
-			continue;
+		if (cookies[c].search(/^\d+,\w+$/) < 0) continue;
 
 		var cookie = cookies[c].split(',');
 
 		try {
 			var usr = new User(cookie[0]);
-			if(usr.number < 1)
-				throw "Invalid user number " + cookie[0] + " in cookie.";
-		} catch(err) {
+			if (usr.number < 1) {
+				throw 'Invalid user number ' + cookie[0] + ' in cookie.';
+			}
+		} catch (err) {
 			log(LOG_DEBUG, err);
 			continue;
 		}
 
 		var session = getSession(usr.number);
-		if(typeof session != "object")
-			continue;
-		if(typeof session.key != "string" || session.key != cookie[1])
+		if (typeof session !== 'object') continue;
+		if (typeof session.key != 'string' || session.key != cookie[1]) {
 			continue;
+		}
 
 		authenticate(usr.alias, usr.security.password);
 		setCookie(usr, session.key);
@@ -81,100 +79,95 @@ var validateSession = function(cookies) {
 
 }
 
-var destroySession = function(cookies) {
+function destroySession(cookies) {
 
-	for(var c in cookies) {
+	for (var c in cookies) {
 
-		if(cookies[c].search(/^\d+,\w+$/) < 0)
-			continue;
+		if (cookies[c].search(/^\d+,\w+$/) < 0)	continue;
 
 		var cookie = cookies[c].split(',');
 
 		try {
 			
 			var usr = new User(cookie[0]);
-			if(usr.number < 1)
-				throw "Invalid user number " + cookie[0] + " in cookie.";
+			if(usr.number < 1) {
+				throw 'Invalid user number ' + cookie[0] + ' in cookie.';
+			}
 
 			var session = getSession(usr.number);
-			if(typeof session != "object")
-				throw "Invalid session for user #" + usr.number;
+			if (typeof session !== 'object') {
+				throw 'Invalid session for user #' + usr.number;
+			}
 
-			if(session.key != cookie[1])
-				throw "Invalid session key for user #" + user.number;
+			if (session.key !== cookie[1]) {
+				throw 'Invalid session key for user #' + user.number;
+			}
 
 			set_cookie(
 				'synchronet',
 				usr.number + ',' + session.key,
 				(time() - settings.timeout),
-				http_request.host.replace(/\:\d*/g, ""),
-				"/"
+				http_request.host.replace(/\:\d*/g, ''),
+				'/'
 			);
 
-			var fn = format("%suser/%04d.web", system.data_dir, usr.number);
+			var fn = format('%suser/%04d.web', system.data_dir, usr.number);
 			file_remove(fn);
 
 			break;
 
-		} catch(err) {
-
-			log("Error destroying session: " + err + ", cookie: " + cookies[c]);
-
+		} catch (err) {
+			log('Error destroying session: ' + err + ', cookie: ' + cookies[c]);
 		}
 
 	}
 
 }
 
-var authenticate = function(alias, password) {
+function authenticate(alias, password) {
 	var un = system.matchuser(alias);
-	if(un < 1)
-		return false;
+	if (un < 1) return false;
 	var usr = new User(un);
-	if(usr.settings&USER_DELETED)
-		return false;
-	if(usr.security.password.toUpperCase() != password.toUpperCase())
+	if (usr.settings&USER_DELETED) return false;
+	if (usr.security.password.toUpperCase() !== password.toUpperCase()) {
 		return false;
+	}
 	login(usr.alias, usr.security.password.toUpperCase());
 	return usr;
 }
 
 // If someone is trying to log in
-if(	typeof http_request.query.username != "undefined"
-	&& 
-	http_request.query.username[0].length <= LEN_ALIAS
-	&&
-	typeof http_request.query.password != "undefined"
-	&&
+if (typeof http_request.query.username !== 'undefined' && 
+	http_request.query.username[0].length <= LEN_ALIAS &&
+	typeof http_request.query.password != 'undefined' &&
 	http_request.query.password[0].length <= LEN_PASS
 ) {
 	var usr = authenticate(
 		http_request.query.username[0],
 		http_request.query.password[0]
 	);
-	if(usr instanceof User)
-		setCookie(usr, randomString(512));
-
+	if (usr instanceof User) setCookie(usr, randomString(512));
 // If they have a cookie
 } else if(
-	typeof http_request.cookie.synchronet != "undefined"
-	&&
-	http_request.cookie.synchronet.some(Function('e', 'return(e.search(/^\\d+,\\w+$/) != -1)'))
+	typeof http_request.cookie.synchronet != 'undefined' &&
+	http_request.cookie.synchronet.some(
+		Function('e', 'return(e.search(/^\\d+,\\w+$/) != -1)')
+	)
 ) {
-
 	// Verify & update their session, or log them out if requested
-	if(typeof http_request.query.logout == "undefined")
+	if (typeof http_request.query.logout === 'undefined') {
 		validateSession(http_request.cookie.synchronet);
-	else
+	} else {
 		destroySession(http_request.cookie.synchronet);
+	}
 }
 
 // If they haven't authenticated as an actual user yet
-if(user.number == 0) {
+if (user.number === 0) {
 
 	// Try to log them in as the guest user
 	var gn = system.matchuser(settings.guest);
-	if(gn > 0) {
+	if (gn > 0) {
 		var gu = new User(gn);
 		login(gu.alias, gu.security.password);
 	} else {
-- 
GitLab


From da287da814c848c69407f780ab713d83995b7dcb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Dec 2015 02:46:59 -0500
Subject: [PATCH 028/752] Give the login form an ID

---
 web/root/index.xjs | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 9357428e9e..394fb8c439 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -109,9 +109,10 @@
 					</ul>
 					<ul class="nav navbar-nav navbar-right">
 					<?xjs
-					if(user.alias == settings.guest || user.number < 1) {
-						if(settings.user_registration)
+					if (user.alias === settings.guest || user.number < 1) {
+						if (settings.user_registration) {
 							writeln('<li><a href="./?page=000-register.xjs">Register</a></li>');
+						}
 						writeln(
 							'<li class="dropdown">' +
 							'<a	href="#" ' +
@@ -122,7 +123,7 @@
 								'aria-expanded="false">' +
 							'Log in<span class="caret"></span></a>' +
 							'<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom: 0px;">' +
-							'<form>' +
+							'<form id="form-login">' +
 							'<label for="input-username" class="sr-only">Username</label>' +
 							'<input id="input-username" title="Username" type="text" class="dropdown form-control" placeholder="Username">' +
 							'<label for="input-password" class="sr-only">Password</label>' +
@@ -197,4 +198,4 @@
 
 	</body>
 
-</html>
\ No newline at end of file
+</html>
-- 
GitLab


From db108f06db97e3f6c13ac82e76cd90a922f0865e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Dec 2015 02:48:17 -0500
Subject: [PATCH 029/752] Prevent the login form from actually submitting
 synchronously. Force a full reload of the page on login/logout. Code
 cleanup/style update.

---
 web/root/js/common.js | 102 +++++++++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 42 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index ff9b48fd6f..ba1a408772 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -1,69 +1,79 @@
 // How often to check for unread mail, new telegrams (milliseconds)
 var updateInterval = 60000;
 
-var login = function() {
-	if($("#input-username").val() == "" || $("#input-password").val() == "")
+function login(evt) {
+	if ($('#input-username').val() === '' ||
+		$('#input-password').val() === ''
+	) {
 		return;
+	}
+	if (typeof evt !== 'undefined') evt.preventDefault();
 	$.ajax(
-		{	'url' : "./api/auth.ssjs",
-			'method' : "POST",
+		{	'url' : './api/auth.ssjs',
+			'method' : 'POST',
 			'data' : {
-				'username' : $("#input-username").val(),
-				'password' : $("#input-password").val()
+				'username' : $('#input-username').val(),
+				'password' : $('#input-password').val()
 			}
 		}
 	).done(
-		function(data) {
-			if(data.authenticated)
-				window.location.reload();
-			else
-				$("#login-form").append('<p class="text-danger">Login failed</p>');
+		function (data) {
+			if (data.authenticated) {
+				window.location.reload(true);
+			} else {
+				$('#login-form').append(
+					'<p class="text-danger">Login failed</p>'
+				);
+			}
 		}
 	);
 }
 
-var logout = function() {
+function logout() {
 	$.ajax(
-		{	'url' : "./api/auth.ssjs",
-			'method' : "GET",
+		{	'url' : './api/auth.ssjs',
+			'method' : 'GET',
 			'data' : {
 				'logout' : true
 			}
 		}
 	).done(
-		function(data) {
-			if(!data.authenticated)
-				window.location.reload();
+		function (data) {
+			if (!data.authenticated) window.location.reload(true);
 		}
 	);
 }
 
-var scrollUp = function() {
-	if(window.location.hash == "")
-		return;
-	if($('#navbar').length < 1)
-		return;
+function scrollUp() {
+	if (window.location.hash === '') return;
+	if ($('#navbar').length < 1) return;
 	window.scrollBy(0, -document.getElementById('navbar').offsetHeight);
 }
 
-var getMailUnreadCount = function() {
+function getMailUnreadCount() {
 	$.getJSON(
-		"./api/forum.ssjs?call=get-mail-unread-count",
-		function(data) {
-			$("#badge-unread-mail").text(data.count < 1 ? "" : data.count);
-			$("#badge-unread-mail-inner").text(data.count < 1 ? "" : data.count);
+		'./api/forum.ssjs?call=get-mail-unread-count',
+		function (data) {
+			$('#badge-unread-mail').text(data.count < 1 ? '' : data.count);
+			$('#badge-unread-mail-inner').text(
+				data.count < 1 ? '' : data.count
+			);
 		}
 	);
 }
 
-var sendTelegram = function(alias) {
-	$('#popUpModalTitle').html("Send a telegram to " + alias);
-	$('#popUpModalBody').html('<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">');
+function sendTelegram(alias) {
+	$('#popUpModalTitle').html('Send a telegram to ' + alias);
+	$('#popUpModalBody').html(
+		'<input type="text" class="form-control" ' +
+		'placeholder="My message" name="telegram" id="telegram">'
+	);
 	$('#popUpModalActionButton').show();
 	$('#popUpModalActionButton').click(
-		function() {
+		function () {
 			$.getJSON(
-				'./api/system.ssjs?call=send-telegram&user=' + alias + '&telegram=' + $('#telegram').val(),
+				'./api/system.ssjs?call=send-telegram&user=' +
+				alias + '&telegram=' + $('#telegram').val(),
 				function(data) {}
 			);
 			$('#popUpModal').modal('hide');
@@ -72,14 +82,21 @@ var sendTelegram = function(alias) {
 	$('#popUpModal').modal('show');
 }
 
-var getTelegram = function() {
+function getTelegram() {
 	$.getJSON(
 		'./api/system.ssjs?call=get-telegram',
-		function(data) {
-			if(data.telegram === null)
+		function (data) {
+			if (typeof data.telegram === 'undefined' ||
+				data.telegram === null
+			) {
 				return;
-			var tg = data.telegram.replace(/\1./g, '').replace(/\r?\n/g, '<br>');
-			$('#popUpModalTitle').html("New telegram(s) received");
+			}
+			var tg = data.telegram.replace(
+				/\1./g, ''
+			).replace(
+				/\r?\n/g, '<br>'
+			);
+			$('#popUpModalTitle').html('New telegram(s) received');
 			$('#popUpModalBody').append(tg);
 			$('#popUpModalActionButton').hide();
 			$('#popUpModal').modal('show');
@@ -87,21 +104,22 @@ var getTelegram = function() {
 	);
 }
 
-window.onload =	function() { 
+window.onload =	function () { 
 
-	$("#button-logout").click(logout);
-	$("#button-login").click(login);
+	$('#button-logout').click(logout);
+	$('#button-login').click(login);
+	$('#form-login').submit(login);
 
 	$('#popUpModal').on(
 		'hidden.bs.modal',
-		function(e) {
+		function (e) {
 			$('#popUpModalActionButton').off('click');
 			$('#popUpModalTitle').empty();
 			$('#popUpModalBody').empty();
 		}
 	);
 	$("#popUpModalCloseButton").click(
-		function() {
+		function () {
 			$('#popUpModal').modal('hide');
 		}
 	);
-- 
GitLab


From 83cfa12d9d7721d2c622cf6717b85577339c42fe Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Dec 2015 02:49:12 -0500
Subject: [PATCH 030/752] !==, style changes.

---
 web/root/api/auth.ssjs | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 0c6495b6c0..7ca3eab841 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -2,12 +2,10 @@ load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'auth.js');
 
 var response = JSON.stringify(
-	{	'authenticated' : (user.alias !== settings.guest),
-		'user' : (user.alias === settings.guest ? 0 : user.number)
-	}
+	{ 'authenticated' : (user.alias !== settings.guest) }
 );
 
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = response.length;
+http_reply.header["Content-Type"] = "application/json";
+http_reply.header["Content-Length"] = response.length;
 
-write(response);
\ No newline at end of file
+write(response);
-- 
GitLab


From 312f43e810b3398ba25747c1f4b021977210b60a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Dec 2015 14:51:30 -0500
Subject: [PATCH 031/752] Include from_ext (user number) in header (postNew()).

---
 web/lib/forum.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 12ed8d2c20..b42d84b07d 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -295,9 +295,10 @@ function postNew (sub, to, subject, body) {
         return false;
     }
     var header = {
-        'to' : to,
-        'from' : user.alias,
-        'subject' : subject
+        to : to,
+        from : user.alias,
+        from_ext : user.number,
+        subject : subject
     };
     if (sub === 'mail') {
         return postMail(header, body);
-- 
GitLab


From 5bdd920886d099aaf6ab5f7851f62aba43e2dff6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Dec 2015 16:22:07 -0500
Subject: [PATCH 032/752] Set to_ext if mail is local in postMail()

---
 web/lib/forum.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index b42d84b07d..228cd4f298 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -323,9 +323,12 @@ function postMail (header, body) {
     var ret = false;
     if (user.number < 1 || user.alias === settings.guest) return ret;
     var na = netaddr_type(header.to);
-    if (na > 0) {
-        header.to_net_type = na;
-        header.to_net_addr = header.to;
+    header.to_net_type = na;
+    if (na > 0) header.to_net_addr = header.to;
+    if (na === NET_NONE) {
+        var un = system.matchuser(header.to);
+        if (un === 0) return false; // Should actually inform about this
+        header.to_ext = un;
     }
     var msgBase = new MsgBase('mail');
     if (msgBase.open()) {
-- 
GitLab


From 0220f8711586fa793d1d37d5e604771d89ce8ecf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 9 Dec 2015 17:35:35 -0500
Subject: [PATCH 033/752] Run body through lfexpand in postNew()

---
 web/lib/forum.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 228cd4f298..74b749d05b 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -300,6 +300,7 @@ function postNew (sub, to, subject, body) {
         from_ext : user.number,
         subject : subject
     };
+    body = lfexpand(body);
     if (sub === 'mail') {
         return postMail(header, body);
     } else {
-- 
GitLab


From c2b5746ecac7ec48edce288e49390aa10be9aa27 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 9 Dec 2015 23:34:32 -0500
Subject: [PATCH 034/752] Use lfexpand() in smarter places.

---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 74b749d05b..0328644747 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -272,6 +272,7 @@ function postMessage (sub, header, body) {
         return ret;
     }
     try {
+        body = lfexpand(body);
         var msgBase = new MsgBase(sub);
         msgBase.open();
         ret = msgBase.save_msg(header, word_wrap(body));
@@ -300,7 +301,6 @@ function postNew (sub, to, subject, body) {
         from_ext : user.number,
         subject : subject
     };
-    body = lfexpand(body);
     if (sub === 'mail') {
         return postMail(header, body);
     } else {
@@ -333,7 +333,7 @@ function postMail (header, body) {
     }
     var msgBase = new MsgBase('mail');
     if (msgBase.open()) {
-        ret = msgBase.save_msg(header, word_wrap(body));
+        ret = msgBase.save_msg(header, lfexpand(body));
         msgBase.close();
     }
     return ret;
-- 
GitLab


From f086dbad4bc1b50afa97f33c7e8fe5654e069c99 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 00:53:38 -0500
Subject: [PATCH 035/752] Return pages with a "primary" property to determine
 placement in primary/secondary menu if desired.

---
 web/lib/pages.js | 102 ++++++++++++++++++++---------------------------
 1 file changed, 44 insertions(+), 58 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 31f6a3176b..84f56f7e40 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -1,9 +1,8 @@
-var getWebCtrl = function() {
-	if(!file_exists(settings.web_root + "pages/webctrl.ini"))
-		return false;
-	var f = new File(settings.web_root + "pages/webctrl.ini");
-	if(!f.open("r")) {
-		log("Unable to open pages/webctrl.ini");
+function getWebCtrl() {
+	if (!file_exists(settings.web_root + 'pages/webctrl.ini')) return false;
+	var f = new File(settings.web_root + 'pages/webctrl.ini');
+	if (!f.open('r')) {
+		log('Unable to open pages/webctrl.ini');
 		exit();
 	}
 	var ini = f.iniGetAllObjects();
@@ -11,12 +10,11 @@ var getWebCtrl = function() {
 	return ini;
 }
 
-var webCtrlTest = function(ini, filename) {
+function webCtrlTest(ini, filename) {
 	var ret = true;
-	for(var i = 0; i < ini.length; i++) {
-		if(!wildmatch(false, filename, ini[i].name))
-			continue;
-		if(	typeof ini[i].AccessRequirements == "undefined"
+	for (var i = 0; i < ini.length; i++) {
+		if (!wildmatch(false, filename, ini[i].name)) continue;
+		if (typeof ini[i].AccessRequirements === 'undefined'
 			||
 			user.compare_ars(ini[i].AccessRequirements)
 		) {
@@ -28,40 +26,33 @@ var webCtrlTest = function(ini, filename) {
 	return ret;
 }
 
-var webCtrlFilter = function(pages) {
+function webCtrlFilter(pages) {
 	var ini = getWebCtrl();
-	if(typeof ini == "boolean" && !ini)
-		return pages;
+	if (typeof ini === 'boolean' && !ini) return pages;
 	pages = pages.filter(
-		function(page) {
+		function (page) {
 			return webCtrlTest(ini, page.page);
 		}
 	);
 	return pages;
 }
 
-var getPages = function(primary) {
-
-	if(typeof primary == "undefined")
-		var wc = "*";
-	else if(!primary)
-		var wc = "*_*";
-	else
-		var wc = "*-*";
+function getPages() {
 
 	var pages = [];
-	var d = directory(settings.web_root + "pages/" + wc);
+	var d = directory(settings.web_root + 'pages/*');
 	d.forEach(
-		function(item) {
-			if(file_isdir(item))
-				return;
+		function (item) {
+			if (file_isdir(item)) return;
 			var fn = file_getname(item);
 			var title = getPageTitle(item);
-			if(typeof title == "undefined" || title.search(/^HIDDEN/) == 0)
+			if (typeof title === 'undefined' || title.search(/^HIDDEN/) === 0) {
 				return;
+			}
 			pages.push(
 				{	'page' : fn,
-					'title' : title
+					'title' : title,
+					'primary' : (fn.search(/^\d+_/) === 0 ? false : true)
 				}
 			);
 		}
@@ -70,7 +61,7 @@ var getPages = function(primary) {
 
 }
 
-var getPageTitle = function(file) {
+function getPageTitle(file) {
 
 	var ext = file_getext(file).toUpperCase();
 
@@ -79,66 +70,61 @@ var getPageTitle = function(file) {
 	var i = f.readAll();
 	f.close();
 
-	if(ext == ".JS" || (ext == ".SSJS" && file.search(/\.xjs\.ssjs$/i)==-1)) {
-		var title = i[0].replace(/\/\//g, "");
+	if (ext == '.JS' || (ext == '.SSJS' && file.search(/\.xjs\.ssjs$/i)==-1)) {
+		var title = i[0].replace(/\/\//g, '');
 		return title;
 	}
 
-	if(ext == ".HTML" || ext == ".XJS") {
+	if (ext === '.HTML' || ext === '.XJS') {
 		// Seek first comment line in an HTML document
-		for(var j = 0; j < i.length; j++) {
+		for (var j = 0; j < i.length; j++) {
 			var k = i[j].match(/^\<\!\-\-.*\-\-\>$/);
-			if(k === null)
-				continue;
-			var title = k[0].replace(/[\<\!\-+|\-+\>]/g, "");
+			if (k === null) continue;
+			var title = k[0].replace(/[\<\!\-+|\-+\>]/g, '');
 			return title;
 		}
 	}
 
-	if(ext == ".TXT")
-		return file_getname(file);
+	if (ext == '.TXT') return file_getname(file);
 
 }
 
-var getPage = function(page) {
+function getPage(page) {
 
-	var ret = "";
+	var ret = '';
 
-	page = settings.web_root + "pages/" + page;
+	page = settings.web_root + 'pages/' + page;
 
-	if(!file_exists(page))
-		return ret;
+	if (!file_exists(page)) return ret;
 
 	var ext = file_getext(page).toUpperCase();
 
-	if(user.alias != settings.guest) {
+	if (user.alias != settings.guest) {
 		var title = getPageTitle(page);
-		if(title != "HIDDEN")
-			setSessionValue(user.number, 'action', title);
+		if (title != 'HIDDEN') setSessionValue(user.number, 'action', title);
 	}
 
 	switch(ext) {
-		case ".SSJS":
-			if(ext == ".SSJS" && page.search(/\.xjs\.ssjs$/i) >= 0)
-				break;
+		case '.SSJS':
+			if (ext === '.SSJS' && page.search(/\.xjs\.ssjs$/i) >= 0) break;
 			load(page, true);
 			break;
-		case ".XJS":
+		case '.XJS':
 			load(xjs_compile(page), true);
 			break;
-		case ".HTML":
+		case '.HTML':
 			var f = new File(page);
-			f.open("r");
-			if(f.is_open) {
+			f.open('r');
+			if (f.is_open) {
 				ret = f.read();
 				f.close();
 			}
 			break;
-		case ".TXT":
+		case '.TXT':
 			var f = new File(page);
-			f.open("r");
-			if(f.is_open) {
-				ret = "<pre>" + f.read() + "</pre>";
+			f.open('r');
+			if (f.is_open) {
+				ret = '<pre>' + f.read() + '</pre>';
 				f.close();
 			}
 			break;
-- 
GitLab


From a8a461b1689d141bbfce5178e718d2ca0ce56e01 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 00:54:13 -0500
Subject: [PATCH 036/752] Handle new getPages return value.

---
 web/root/index.xjs | 52 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 16 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 394fb8c439..c3abe5f83d 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,15 +1,18 @@
 <?xjs
 	load('xjs.js');
-	load(system.exec_dir + "../web/lib/init.js");
-	load(settings.web_lib + "auth.js");
-	load(settings.web_lib + "pages.js");
-	load(settings.web_lib + "sidebar.js");
+	load(system.exec_dir + '../web/lib/init.js');
+	load(settings.web_lib + 'auth.js');
+	load(settings.web_lib + 'pages.js');
+	load(settings.web_lib + 'sidebar.js');
 
-	if(	typeof http_request.query.page == "undefined"
+	if (typeof http_request.query.page === 'undefined'
 		||
-		!file_exists(settings.web_root + "pages/" + file_getname(http_request.query.page[0]))
+		!file_exists(
+			settings.web_root + 'pages/' +
+			file_getname(http_request.query.page[0])
+		)
 	) {
-		var page = "000-home.xjs";
+		var page = '000-home.xjs';
 	} else {
 		var page = file_getname(http_request.query.page[0]);
 	}
@@ -66,11 +69,25 @@
 				<div id="navbar" class="collapse navbar-collapse">
 					<ul class="nav navbar-nav">
 						<?xjs 
-							getPages(true).forEach(
-								function(thePage) {
+							var pages = getPages();
+							var primary = [];
+							var secondary = [];
+							pages.forEach(
+								function (thePage) {
+									if (thePage.primary) {
+										primary.push(thePage);
+									} else {
+										secondary.push(thePage);
+									}
+								}
+							);
+							pages = [];
+							primary.forEach(
+								function (thePage) {
 									writeln(
 										'<li' +
-										(thePage.page == page ? ' class="active"' : "") + 
+										(thePage.page === page
+											? ' class="active"' : "") + 
 										'>' +
 										'<a href="./?page=' + thePage.page + '">' +
 										thePage.title +
@@ -78,8 +95,7 @@
 									);
 								}
 							);
-							var more = getPages(false);
-							if(more.length > 0) {
+							if (secondary.length > 0) {
 								writeln(
 									'<li class="dropdown">' +
 									'<a	href="#" ' +
@@ -91,11 +107,12 @@
 									'More<span class="caret"></span></a>' +
 									'<ul class="dropdown-menu">'
 								);
-								more.forEach(
-									function(thePage) {
+								secondary.forEach(
+									function (thePage) {
 										writeln(
 											'<li' +
-											(thePage.page == page ? ' class="active"' : "") + 
+											(thePage.page === page
+												? ' class="active"' : "") + 
 											'>' +
 											'<a href="./?page=' + thePage.page + '">' +
 											thePage.title +
@@ -174,8 +191,11 @@
 					<div class="row">
 					<?xjs 
 						var ini = getWebCtrl();
-						if((typeof ini == "boolean" && !ini) || webCtrlTest(ini, page))
+						if ((typeof ini == "boolean" && !ini) ||
+							webCtrlTest(ini, page)
+						) {
 							write(getPage(page));
+						}
 					?>
 					</div>
 				</div>
-- 
GitLab


From a5c659148009ec63c0e62903727cf92fd7626267 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 01:03:05 -0500
Subject: [PATCH 037/752] Deuce likes page titles

---
 web/root/index.xjs | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index c3abe5f83d..0751b2fa8c 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -28,7 +28,11 @@
 		<meta name="description" content="">
 		<meta name="author" content="">
 		<link rel="icon" href="./images/favicon.ico">
-		<title><?xjs write(system.name); ?></title>
+		<title>
+			<?xjs write(getPageTitle(settings.web_root + 'pages/' + page)); ?>
+			:
+			<?xjs write(system.name); ?>
+		</title>
 		<link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
 		<link href="./css/offcanvas.css" rel="stylesheet">
 		<link href="./css/style.css" rel="stylesheet">
-- 
GitLab


From 14ce2f31e4ccdae6d76250b402c6880369e3a96b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 02:00:19 -0500
Subject: [PATCH 038/752] Make rlogin xtrn value in termtype compatible with
 stock logon.js

---
 mods/websocket-rlogin-service.js | 84 +++++++++++++++++---------------
 1 file changed, 46 insertions(+), 38 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 109b826884..d14db81a7b 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -1,19 +1,17 @@
-load("sbbsdefs.js");
-load("websocket-proxy.js");
+load('sbbsdefs.js');
+load('websocket-proxy.js');
 
-var err = function(msg) {
+function err(msg) {
 	log(LOG_DEBUG, msg);
 	client.socket.close();
 	exit();
 }
 
-var getSession = function(un) {
-	var fn = format("%suser/%04d.web", system.data_dir, un);
-	if(!file_exists(fn))
-		return false;
+function getSession(un) {
+	var fn = format('%suser/%04d.web', system.data_dir, un);
+	if (!file_exists(fn)) return false;
 	var f = new File(fn);
-	if(!f.open("r"))
-		return false;
+	if (!f.open('r')) return false;
 	var session = f.iniGetObject();
 	f.close();
 	return session;
@@ -27,62 +25,72 @@ try {
 
 	wss = new WebSocketProxy(client);
 
-	if(typeof wss.headers["Cookie"] == "undefined")
-		err("No cookie from WebSocket client.");
+	if (typeof wss.headers['Cookie'] === 'undefined') {
+		err('No cookie from WebSocket client.');
+	}
 
-	var cookie = wss.headers["Cookie"].split("=");
-	if(cookie[0] != "synchronet" || cookie.length < 2)
-		err("Invalid cookie from WebSocket client.");
+	var cookie = wss.headers['Cookie'].split('=');
+	if (cookie[0] !== "synchronet" || cookie.length < 2) {
+		err('Invalid cookie from WebSocket client.');
+	}
 
-	cookie = cookie[1].split(",");
+	cookie = cookie[1].split(',');
 	cookie[0] = parseInt(cookie[0]);
-	if(cookie.length < 2 || isNaN(cookie[0]) || cookie[0] < 1 || cookie[0] > system.lastuser)
-		err("Invalid cookie from WebSocket client.");
+	if (cookie.length < 2 ||
+		isNaN(cookie[0]) ||
+		cookie[0] < 1 ||
+		cookie[0] > system.lastuser
+	) {
+		err('Invalid cookie from WebSocket client.');
+	}
 
 	var usr = new User(cookie[0]);
 	var session = getSession(usr.number);
-	if(!session)
-		err("Unable to read web session file for user #" + usr.number);
-	if(cookie[1] != session.key)
-		err("Session key mismatch for user #" + usr.number);
-	if(typeof session.xtrn != "string" || typeof xtrn_area.prog[session.xtrn] == "undefined")
-		err("Invalid external program code.");
+	if (!session) {
+		err('Unable to read web session file for user #' + usr.number);
+	}
+	if (cookie[1] != session.key) {
+		err('Session key mismatch for user #' + usr.number);
+	}
+	if (typeof session.xtrn !== 'string' ||
+		typeof xtrn_area.prog[session.xtrn] === 'undefined'
+	) {
+		err('Invalid external program code.');
+	}
 
 	var f = new File(file_cfgname(system.ctrl_dir, 'sbbs.ini'));
-	if(!f.open("r"))
-		err("Unable to open sbbs.ini.");
-	var ini = f.iniGetObject("BBS");
+	if (!f.open('r')) err('Unable to open sbbs.ini.');
+	var ini = f.iniGetObject('BBS');
 	f.close();
 
 	rlogin = new RLogin(
-		{	'host' : system.inet_addr,
-			'port' : ini.RLoginPort,
-			'clientUsername' : usr.security.password,
-			'serverUsername' : usr.alias,
-			'terminalType' : session.xtrn,
-			'terminalSpeed' : 115200
+		{	host : system.inet_addr,
+			port : ini.RLoginPort,
+			clientUsername : usr.security.password,
+			serverUsername : usr.alias,
+			terminalType : 'xtrn=' + session.xtrn,
+			terminalSpeed : 115200
 		}
 	);
 	rlogin.connect();
-	log(LOG_DEBUG, usr.alias + " logged on via RLogin for " + session.xtrn);
+	log(LOG_DEBUG, usr.alias + ' logged on via RLogin for ' + session.xtrn);
 
-	while(client.socket.is_connected && rlogin.connected) {
+	while (client.socket.is_connected && rlogin.connected) {
 
 		wss.cycle();
 		rlogin.cycle();
 
 		var send = rlogin.receive();
-		if(send.length > 0)
-			wss.send(send);
+		if (send.length > 0) wss.send(send);
 
-		while(wss.data_waiting) {
+		while (wss.data_waiting) {
 			var data = wss.receiveArray();
 			rlogin.send(data);
 		}
 
 	}
 
-} catch(err) {
+} catch (err) {
 
 	log(err);
 
-- 
GitLab


From 9895b25d7cd5cd85b9af30407002b60a66b4a8d1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 02:07:21 -0500
Subject: [PATCH 039/752] Notes about the rlogin games change.

---
 README.md | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 029bf00b4a..3ff47d8628 100644
--- a/README.md
+++ b/README.md
@@ -69,15 +69,27 @@ Command=websocket-rlogin-service.js
 - Tell your router and firewall to open and forward ports *1123* and *1513* to your BBS
 - If you were running ecWeb v3 and modified the *RootDirectory* value in the *[Web]* section of *ctrl/sbbs.ini* to point to *../web/root/ecwebv3*, change it back to *../web/root*.
 
-- Open your *mods/logon.js* file *or, if it doesn't exist yet* copy your *exec/logon.js* file to *mods/logon.js* and then open it
-- Add the following block of code to the top of the file, under the line that says *load('sbbsdefs.js')*:
+- Edit the *[logon]* section of your *ctrl/modopts.ini* file, and ensure that it has an *rlogin_auto_xtrn* key with a value of *true*
+	- NB: that's the *[logon]* section and not the *[login]* section
+
+```ini
+[logon]
+rlogin_auto_xtrn = true
+```
+
+- Your *logon.js* file should have a block of code near the top that looks like this, but if it doesn't you should add it in:
+
 ```js
-if (bbs.connection.toUpperCase() == "RLOGIN" &&
-   typeof xtrn_area.prog[console.terminal] != "undefined"
-) {
-        bbs.exec_xtrn(console.terminal);
-        bbs.hangup();
-        exit();
+var options = load("modopts.js", "logon");
+
+// Check if we're being asked to auto-run an external (web interface external programs section uses this)
+if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (console.terminal.indexOf("xtrn=") === 0)) {
+    var external_code = console.terminal.substring(5);
+    if (!bbs.exec_xtrn(external_code)) {
+        alert(log(LOG_ERR,"!ERROR Unable to launch external: '" + external_code + "'"));
+    }
+    bbs.hangup();
+	exit();
 }
 ```
 
-- 
GitLab


From 6ab045cfccb2d6017b2397b40a4fbb8a9fc61b42 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 02:07:47 -0500
Subject: [PATCH 040/752] Notes about the rlogin games change.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 3ff47d8628..e81e18da9c 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ Command=websocket-rlogin-service.js
 rlogin_auto_xtrn = true
 ```
 
-- Your *logon.js* file should have a block of code near the top that looks like this, but if it doesn't you should add it in:
+	- Your *logon.js* file should have a block of code near the top that looks like this, but if it doesn't you should add it in:
 
 ```js
 var options = load("modopts.js", "logon");
-- 
GitLab


From ac7aeceeb973c5d5fdb7c3891a0415c6c6eea0c1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 02:08:13 -0500
Subject: [PATCH 041/752] Notes about the rlogin games change.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index e81e18da9c..3ff47d8628 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ Command=websocket-rlogin-service.js
 rlogin_auto_xtrn = true
 ```
 
-	- Your *logon.js* file should have a block of code near the top that looks like this, but if it doesn't you should add it in:
+- Your *logon.js* file should have a block of code near the top that looks like this, but if it doesn't you should add it in:
 
 ```js
 var options = load("modopts.js", "logon");
-- 
GitLab


From 28305aaf41e9186c6d8cef50a08facb922c23f8e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 02:30:50 -0500
Subject: [PATCH 042/752] Better striping.

---
 web/root/css/style.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index ea31845b02..1d4df193aa 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -55,7 +55,7 @@ span.badge.total {
 	margin: 0 1em 1em 0;
 }
 
-.striped:nth-of-type(even) {
+.striped:nth-of-type(even), .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th {
 	background: #F2F2F2;
 }
 
-- 
GitLab


From 48eda40e2c0c64e30c0d72fe522789f009e5b5d2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 03:03:10 -0500
Subject: [PATCH 043/752] Consolidated styling of hovered list-group-item a and
 li tags.

---
 web/root/css/style.css | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 1d4df193aa..e88474ed1b 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -1,14 +1,10 @@
-a.list-group-item:hover {
+a.list-group-item:hover, a.list-group-item:active, li.list-group-item.mail:hover {
 	background-color: #A9E2F3;
 }
 
 a.list-group-item.scanned:hover {
 }
 
-a.list-group-item.active {
-	background-color: #A9E2F3;
-}
-
 a.list-group-item.scanned:hover {
 }
 
@@ -36,10 +32,6 @@ input.dropdown {
 	margin: 0 0 1em 0;
 }
 
-li.list-group-item.mail:hover {
-	background-color: #A9E2F3;
-}
-
 span.message-header.unread {
 	background-color: #FCF8E3;
 }
-- 
GitLab


From c4379fd0e120037bf6f0e3fd1f1b2cacb9dd18a4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 03:22:37 -0500
Subject: [PATCH 044/752] Some comments and shuffling-around of things.

---
 web/root/css/style.css | 51 +++++++++++++++++++++++++-----------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index e88474ed1b..bebe652486 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -1,54 +1,65 @@
-a.list-group-item:hover, a.list-group-item:active, li.list-group-item.mail:hover {
-	background-color: #A9E2F3;
+/* Alternating rows in lists of messages, Who's Online, etc. */
+.striped:nth-of-type(even), .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th {
+	background: #F2F2F2;
 }
 
-a.list-group-item.scanned:hover {
+/* A link in a list when the mouse is hovering over it (mostly applies to the Forum) */
+a.list-group-item:hover, a.list-group-item:active, li.list-group-item.mail:hover {
+	background-color: #A9E2F3;
 }
 
+/* A link to a scanned sub in a list, should you wish to style it, I guess. */
 a.list-group-item.scanned:hover {
 }
 
+/* An unread mail message in the list view. */
 a.unread {
 	background: #FFFFFF;
 }
 
+/* A read mail message in the list view. */
 a.read {
 	background: #E6E6E6;
 }
 
-blockquote {
-	font-size: 14px;
-	margin: 7px;
-	padding: 7px;
-	border-left: 5px solid #A4A4A4;
+/* Unread-message indicator for scanned subs. */
+span.badge.scanned {
+	background-color: #2E9AFE;
 }
 
-div.message {
-	margin: 1em 0 1em 1em;
+/* Unread-message indicator for all subs. */
+span.badge.total {
 }
 
-input.dropdown {
-	clear: both;
-	margin: 0 0 1em 0;
+/* Inline quoted text in a message. */
+blockquote {
+	font-size: 14px;
+	border-left : 5px solid #A4A4A4;
 }
 
+/* Unused? */
 span.message-header.unread {
 	background-color: #FCF8E3;
 }
 
-span.badge.scanned {
-	background-color: #2E9AFE;
+/*	You probably don't need to mess with rules below this line. */
+
+blockquote {
+	margin: .5em;
+	padding: .5em;
 }
 
-span.badge.total {
+div.message {
+	margin: 1em 0 1em 1em;
 }
 
-.icon {
-	margin: 0 1em 1em 0;
+input.dropdown {
+	clear: both;
+	margin: 0 0 1em 0;
 }
 
-.striped:nth-of-type(even), .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th {
-	background: #F2F2F2;
+.icon {
+	margin: 0 1em 1em 0;
 }
 
 .reply {
-- 
GitLab


From 8d945d92e1c9520e075df17d9cc06b747830d730 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Dec 2015 03:43:33 -0500
Subject: [PATCH 045/752] Loading icon for unread message indicators.

---
 web/root/images/ajax-loader-small.gif | Bin 0 -> 723 bytes
 web/root/pages/001-forum.ssjs         |  12 +++++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)
 create mode 100644 web/root/images/ajax-loader-small.gif

diff --git a/web/root/images/ajax-loader-small.gif b/web/root/images/ajax-loader-small.gif
new file mode 100644
index 0000000000000000000000000000000000000000..09d621ede9dbe610877292e554c858d60573359e
GIT binary patch
literal 723
zcmZ?wbhEHb6ky<H_`<;O|Nnmm28LU=Ze6%=;nk~GY;0@{3=DdDdQDAD2?+_icI{G9
zQu6Zh>gwuBNlDqaZ=af)8Vd`HpP%2eXU}fjxbfx77YhrEJ9qAU`t)hrwrvX*EI4)Q
z)Vp`@7+^s0KewN2NU*bGfUA+70W%{51B2o}Dd(cp#FEq$h4Rdj428t3#EP8!#1y^E
zytI4<#h)yk+zbp1Iv^83j$~lfRZ!?l$(*-j0oS8bOzRv3nk~+l7F@2rcYDWtKK3Vq
z?OPr>oZ!(bUU!Ylb?r1KY$m8ABucO*%v+Hu(>`(Efm?@GzPYr#_xYQ>uXji2Ys_J7
zv=;1?V8W)+OhKWqJ5lFxhYF{X6i@P!q(vGhrO$8aeQhLlU^?@GroGW0Pk0M9p1pW^
zhp^wwL!sPSBGdd131BnIR6(I{35Tm-2vhH4lOz?-i?3|@h0m8(KDVC5yPWw%i(S$N
z_g|v@XC@X~vPe~~zSg}<Yi^JtHj|7X;c;zK#50$`$vZqQB?X1Lp4<}q$SUsbwA+kn
zhvNfZC^Pn+cy@v{d7jtGt6!zC`pq0-j7ni_%Zg1&%QA1aCQi?Iyl7kQtXMvUH-DS9
zCkj4b)|>9D(m%t5DPEVs%<S}neGh^-usKN|V%8Ivz{x8%y<1fzqZ+h@_krp9-`iyh
irgMMmIx?%URpwWmE`tf1bL9FZSObV;Gps>L1dIVm|NmY9

literal 0
HcmV?d00001

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index e037341835..2c54c60743 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -276,7 +276,9 @@ if(	typeof http_request.query.sub != "undefined"
 			format(
 				'<a href="./?page=%s&amp;sub=%s" class="list-group-item striped%s">' +
 				'<h4><strong>%s</strong></h4>' +
-				'<span title="Unread messages" class="badge %s" id="badge-%s"></span>' +
+				'<span title="Unread messages" class="badge %s" id="badge-%s">' +
+				'<img src="./images/ajax-loader-small.gif">' +
+				'</span>' +
 				'<p>%s</p>' +
 				'</a>',
 				http_request.query.page[0],
@@ -332,8 +334,12 @@ if(	typeof http_request.query.sub != "undefined"
 			format(
 				'<a href="./?page=%s&amp;group=%s" class="list-group-item striped">' +
 				'<h3><strong>%s</strong></h3>' +
-				'<span title="Unread messages (other)" class="badge ignored" id="badge-ignored-%s"></span>' +
-				'<span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-%s"></span>' +
+				'<span title="Unread messages (other)" class="badge ignored" id="badge-ignored-%s">' +
+				'<img src="./images/ajax-loader-small.gif">' +
+				'</span>' +
+				'<span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-%s">' +
+				'<img src="./images/ajax-loader-small.gif">' +
+				'</span>' +
 				'<p>%s : %s sub-boards</p>' +
 				'</a>',
 				http_request.query.page[0],
-- 
GitLab


From 4af486bdd087faf42ec1331ec1d3cfb9a996fb44 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 13 Dec 2015 17:23:36 -0500
Subject: [PATCH 046/752] Some cleanup prior to fixing offset issues.

---
 web/root/pages/003_userlist.xjs | 257 +++++++++++++++++---------------
 1 file changed, 135 insertions(+), 122 deletions(-)

diff --git a/web/root/pages/003_userlist.xjs b/web/root/pages/003_userlist.xjs
index 34c2012288..6ad4c25638 100644
--- a/web/root/pages/003_userlist.xjs
+++ b/web/root/pages/003_userlist.xjs
@@ -1,95 +1,80 @@
 <!--User List-->
-<!--Slightly modified version of the ecWeb v3 userlist-->
 <?xjs
-	if(typeof argv[0] != "boolean" || !argv[0])
-		exit();
+	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+
+	var pageSize = 500;
 
 	// Uncomment/comment properties below to enable/disable list columns
+	// Rearrange to change order of appearance
 	var columns = {
-//		number : { name : "#", type : "number" },
-		alias : { name : "Alias", type : "string" },
-//		name : { name : "Name", type : "string" },
-//		age : { name : "Age", type : "number" },
-//		gender : { name : "Sex", type : "string" },
-		location : { name : "Location", type : "string" },
-		laston_date : { name : "Last On", type : "date" },
-		connection : { name : "Via", type : "string" },
-//		firston_date : { name : "First On", type : "date" },
-//		total_logons : { name : "Calls", type : "number" },
-//		total_posts : { name : "Posts", type : "number" }
+		// number : { name : "#", type : "number" },
+		alias : { name : 'Alias', type : 'string' },
+		// name : { name : "Name", type : "string" },
+		// age : { name : "Age", type : "number" },
+		// gender : { name : "Sex", type : "string" },
+		location : { name : 'Location', type : 'string' },
+		laston_date : { name : 'Last On', type : 'date' },
+		// connection : { name : "Via", type : "string" },
+		firston_date : { name : "First On", type : "date" },
+		// total_logons : { name : "Calls", type : "number" },
+		// total_posts : { name : "Posts", type : "number" }
 	};
 
-	var pageSize = 500;
-
 	// Most people won't need to edit below this line
-	load("sbbsdefs.js");
+	load('sbbsdefs.js');
 
-	if(	typeof http_request.query.offset == "undefined"
-		||
-		isNaN(parseInt(http_request.query.offset[0]))
-		||
-		http_request.query.offset[0] < 1
+	if (typeof http_request.query.offset === 'undefined' ||
+		isNaN(parseInt(http_request.query.offset[0])) ||
+		parseInt(http_request.query.offset[0]) < 1
 	) {
 		var offset = 1;
 	} else {
 		var offset = parseInt(http_request.query.offset[0]);
 	}
 
-	var previousOffset = Math.max(1, offset - pageSize);
-	var nextOffset = Math.min(offset + pageSize, offset + (system.lastuser - offset));
-
-	var users = [];
 	var url = format(
-		"http://%s%s",
+		'http://%s%s',
 		http_request.host,
 		http_request.request_string.split("&")[0]
 	);
 	
-	var sortUser = function(a, b, sortOrder, type) {
-		if(type == "string") {
+	function sortUser(a, b, sortOrder, type) {
+		if (type === 'string') {
 			a = a.toUpperCase();
 			b = b.toUpperCase();
-			ret =	(a < b)
-					?
-					((sortOrder == "ascending") ? 1 : -1)
-					:
-					(	(a > b)
-						?
-						((sortOrder == "ascending") ? -1 : 1)
-						:
-						0
-					);
-		} else if(type == "number" || type == "date") {
-			ret =	(a < b)
-					?
-					((sortOrder == "ascending") ? -1 : 1)
-					:
-					(	(a > b)
-						?
-						((sortOrder == "ascending") ? 1 : -1)
-						:
-						0
-					);
+			ret = (
+				a < b
+				? (sortOrder === 'ascending' ? 1 : -1)
+				: (	a > b
+					? (sortOrder === 'ascending' ? -1 : 1)
+					: 0
+				)
+			);
+		} else if (type === 'number' || type === 'date') {
+			ret = (
+				a < b
+				? (sortOrder === 'ascending' ? -1 : 1)
+				: ( a > b
+					? (sortOrder === 'ascending' ? 1 : -1)
+					: 0
+				)
+			);
 		}
 		return ret;
 	}
 
-	var sortUsers = function(a, b) {
+	function sortUsers(a, b) {
 		var ret = 0;
-		if(	typeof http_request.query.sortby != "undefined"
-			&&
-			typeof http_request.query.sortorder != "undefined"
-			&&
-			(	http_request.query.sortorder == "ascending"
-				||
-				http_request.query.sortorder == "descending"
+		if (typeof http_request.query.sortby !== 'undefined' &&
+			typeof http_request.query.sortorder !== 'undefined'	&&
+			(	http_request.query.sortorder[0] === 'ascending' ||
+				http_request.query.sortorder[0] === 'descending'
 			)
 		) {
-			var sortBy = http_request.query.sortby.toString().toLowerCase();
-			var sortOrder = http_request.query.sortorder.toString().toLowerCase();
-			for(var c in columns) {
-				if(sortBy != c)
-					continue;
+			var sortBy = http_request.query.sortby[0].toLowerCase();
+			var sortOrder = http_request.query.sortorder[0].toLowerCase();
+			for (var c in columns) {
+				if (sortBy != c) continue;
 				ret = sortUser(a[c], b[c], sortOrder, columns[c].type);
 				break;
 			}
@@ -97,14 +82,16 @@
 		return ret;
 	}
 	
-	var makeSortURLs = function(field, order) {
+	function makeSortURLs(field, order) {
 		return format(
-			'<a class="icon" href="./?page=%s&sortby=%s&sortorder=ascending&offset=%s">'
-			+ '<span class="glyphicon glyphicon-arrow-up"></span>'
-			+ '</a>'
-			+ '<a class="icon" href="./?page=%s&sortby=%s&sortorder=descending&offset=%s">'
-			+ '<span class="glyphicon glyphicon-arrow-down"></span>'
-			+ '</a>',
+			'<a class="icon" ' +
+			'href="./?page=%s&sortby=%s&sortorder=ascending&offset=%s">' +
+			'<span class="glyphicon glyphicon-arrow-up"></span>' +
+			'</a>' +
+			'<a class="icon" ' +
+			'href="./?page=%s&sortby=%s&sortorder=descending&offset=%s">' +
+			'<span class="glyphicon glyphicon-arrow-down"></span>' +
+			'</a>',
 			http_request.query.page[0],
 			field,
 			offset,
@@ -113,78 +100,104 @@
 			offset
 		);
 	}
+
+	function makePagerURLs() {
+		var ret = { previous : '', next : '' };
+		var previousOffset = Math.max(1, offset - pageSize);
+		var nextOffset = Math.min(
+			offset + pageSize, offset + (system.lastuser - offset)
+		);
+		if (offset > 1) {
+			ret.previous = format(
+				'<li><a href="./?page=%s&offset=%s">Previous</a></li>',
+				http_request.query.page[0],
+				previousOffset
+			);
+		}
+		if (system.lastuser - offset > pageSize) {
+			ret.next = format(
+				'<li><a href="./?page=%s&offset=%s">Next</a></li>',
+				http_request.query.page[0],
+				nextOffset
+			);
+		}
+		return ret;
+	}
 	
-	var copyProperties = function(source, dest) {
-		for(var property in source) {
-			if(typeof source[property] == "string" || typeof source[property] == "number")
+	function copyProperties(source, dest) {
+		for (var property in source) {
+			if ((	typeof source[property] === 'string' ||
+					typeof source[property] === 'number'
+				) && typeof columns[property] !== 'undefined'
+			) {
 				dest[property] = source[property];
+			} else if (property === 'stats') {
+				for (var stat in source.stats) {
+					if (typeof columns[stat] === 'undefined') continue;
+					dest[stat] = source.stats[stat];
+				}
+			}
 		}
 		return dest;
 	}
 
-	for(var u = offset;
-		u < ((system.lastuser - offset > pageSize) ? offset + pageSize : system.lastuser);
-		u++
-	) {
-		var usr1 = new User(u);
-		if(usr1.settings&USER_DELETED || usr1.compare_ars("REST Q"))
-			continue;
-		var usr2 = copyProperties(usr1, {});
-		usr2 = copyProperties(usr1.stats, usr2);	
-		users.push(usr2);
+	function loadUsers(offset, pageSize) {
+		var users = [];
+		for (var u = 0; u < system.lastuser; u++) {
+			var usr = new User(u);
+			if (usr.settings&USER_DELETED ||
+				usr.compare_ars('REST Q') ||
+				usr.alias === 'Guest' ||
+				usr.alias === settings.guest
+			) {
+				continue;
+			}
+			users.push(copyProperties(usr, {}));
+		}
+		users.sort(sortUsers);
+		return users.slice(offset, offset + pageSize);
+	}
+
+	function writeColumns() {
+		for (var c in columns) {
+			writeln('<th>' + columns[c].name + ' ' + makeSortURLs(c) + '</th>');
+		}
 	}
-	users.sort(sortUsers);
+
+	function writeRows(users) {
+		for (var u = 0; u < users.length; u++) {
+			writeln('<tr>');
+			for (var c in columns) {
+				writeln(
+					'<td>' +
+					(	columns[c].type === 'date'
+						? system.timestr(users[u][c])
+						: users[u][c].toString()
+					) +	'</td>'
+				);
+			}
+			writeln('</tr>');
+		}
+	}
+
+	var pager = makePagerURLs();
+
 ?>
 
 <div class="well well-sm"><h3>User List</h3></div>
 <table class="table table-striped">
 	<thead>
 		<tr>
-<?xjs
-	for(var c in columns)
-		writeln(format("<th>%s %s</th>", columns[c].name, makeSortURLs(c)));
-?>
+			<?xjs writeColumns(); ?>
 		</tr>
 	</thead>
 	<tbody>
-<?xjs
-	for(var u = 0; u < users.length; u++) {
-		writeln('<tr>');
-		for(var c in columns) {
-			writeln(
-				'<td>' +
-				(columns[c].type == "date" ? system.timestr(users[u][c]) : users[u][c].toString()) +
-				'</td>'
-			);
-		}
-		writeln("</tr>");
-	}
-?>
+		<?xjs writeRows(loadUsers(offset, pageSize)); ?>
 	</tbody>
 </table>
 
 <nav>
 	<ul class="pager">
-<?xjs
-	if(offset > 1) {
-		writeln(
-			format(
-				'<li><a href="./?page=%s&offset=%s">Previous</a></li>',
-				http_request.query.page[0],
-				previousOffset
-			)
-		);
-	}
-
-	if(system.lastuser - offset > pageSize) {
-		writeln(
-			format(
-				'<li><a href="./?page=%s&offset=%s">Next</a></li>',
-				http_request.query.page[0],
-				nextOffset
-			)
-		);
-	}
-?>
+		<?xjs writeln(pager.previous); writeln(pager.next); ?>
 	</ul>
 </nav>
\ No newline at end of file
-- 
GitLab


From 09c7d529c03918ccd0470e648ad9099e09ced1ae Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 13 Dec 2015 17:48:13 -0500
Subject: [PATCH 047/752] Fixed start at offset; start at user 1.

---
 web/root/pages/003_userlist.xjs | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/web/root/pages/003_userlist.xjs b/web/root/pages/003_userlist.xjs
index 6ad4c25638..63358a3bfc 100644
--- a/web/root/pages/003_userlist.xjs
+++ b/web/root/pages/003_userlist.xjs
@@ -15,7 +15,7 @@
 		location : { name : 'Location', type : 'string' },
 		laston_date : { name : 'Last On', type : 'date' },
 		// connection : { name : "Via", type : "string" },
-		firston_date : { name : "First On", type : "date" },
+		// firston_date : { name : "First On", type : "date" },
 		// total_logons : { name : "Calls", type : "number" },
 		// total_posts : { name : "Posts", type : "number" }
 	};
@@ -27,7 +27,7 @@
 		isNaN(parseInt(http_request.query.offset[0])) ||
 		parseInt(http_request.query.offset[0]) < 1
 	) {
-		var offset = 1;
+		var offset = 0;
 	} else {
 		var offset = parseInt(http_request.query.offset[0]);
 	}
@@ -103,22 +103,20 @@
 
 	function makePagerURLs() {
 		var ret = { previous : '', next : '' };
-		var previousOffset = Math.max(1, offset - pageSize);
-		var nextOffset = Math.min(
-			offset + pageSize, offset + (system.lastuser - offset)
-		);
 		if (offset > 1) {
 			ret.previous = format(
 				'<li><a href="./?page=%s&offset=%s">Previous</a></li>',
 				http_request.query.page[0],
-				previousOffset
+				Math.max(0, offset - pageSize)
 			);
 		}
 		if (system.lastuser - offset > pageSize) {
 			ret.next = format(
 				'<li><a href="./?page=%s&offset=%s">Next</a></li>',
 				http_request.query.page[0],
-				nextOffset
+				Math.min(
+					offset + pageSize, offset + (system.lastuser - offset)
+				)
 			);
 		}
 		return ret;
@@ -143,7 +141,7 @@
 
 	function loadUsers(offset, pageSize) {
 		var users = [];
-		for (var u = 0; u < system.lastuser; u++) {
+		for (var u = 1; u < system.lastuser; u++) {
 			var usr = new User(u);
 			if (usr.settings&USER_DELETED ||
 				usr.compare_ars('REST Q') ||
-- 
GitLab


From 9e8d397bb4c01e96b30d05c8cf88a4470fb2dd0b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 00:12:10 -0500
Subject: [PATCH 048/752] Post using real name if required by sub settings.

---
 web/lib/forum.js | 66 +++++++++++++++++++++++++-----------------------
 1 file changed, 35 insertions(+), 31 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 0328644747..9256ef5b9d 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -264,14 +264,16 @@ function postMessage (sub, header, body) {
         header.to === '' ||
         typeof header.from !== 'string' ||
         typeof header.subject !== 'string' ||
-// This could be a reply to a message with no subject, apparently
-//      header.subject == '' ||
         typeof body !== 'string' ||
         body === ''
     ) {
         return ret;
     }
     try {
+        if (msg_area.sub[sub].settings&SUB_NAME) {
+            if (user.name === '') return ret;
+            header.from = user.name;
+        }
         body = lfexpand(body);
         var msgBase = new MsgBase(sub);
         msgBase.open();
@@ -283,41 +285,18 @@ function postMessage (sub, header, body) {
     return ret;
 }
 
-// Post a new (non-reply) message to 'sub'
-function postNew (sub, to, subject, body) {
-    if (typeof sub !== 'string' ||
-        typeof to !== 'string' ||
-        to === '' ||
-        typeof subject !== 'string' ||
-        subject === '' ||
-        typeof body !== 'string' ||
-        body === ''
-    ) {
-        return false;
-    }
-    var header = {
-        to : to,
-        from : user.alias,
-        from_ext : user.number,
-        subject : subject
-    };
-    if (sub === 'mail') {
-        return postMail(header, body);
-    } else {
-        return postMessage(sub, header, body);
-    }
-}
-
 // Post a message to the mail sub, if this user can do so
 // Called by postNew/postReply, not directly
 function postMail (header, body) {
     // Lazy ARS checks; we could check the *type* of email being sent, I guess.
-    if (user.security.restrictions&UFLAG_E || user.security.restrictions&UFLAG_M) {
+    if (user.security.restrictions&UFLAG_E ||
+        user.security.restrictions&UFLAG_M
+    ) {
         return false;
     }
-    if (    typeof header.to !== 'string' ||
-            typeof header.subject !== 'string' ||
-            typeof body !== 'string'
+    if (typeof header.to !== 'string' ||
+        typeof header.subject !== 'string' ||
+        typeof body !== 'string'
     ) {
         return false;
     }
@@ -339,6 +318,31 @@ function postMail (header, body) {
     return ret;
 }
 
+// Post a new (non-reply) message to 'sub'
+function postNew (sub, to, subject, body) {
+    if (typeof sub !== 'string' ||
+        typeof to !== 'string' ||
+        to === '' ||
+        typeof subject !== 'string' ||
+        subject === '' ||
+        typeof body !== 'string' ||
+        body === ''
+    ) {
+        return false;
+    }
+    var header = {
+        to : to,
+        from : user.alias,
+        from_ext : user.number,
+        subject : subject
+    };
+    if (sub === 'mail') {
+        return postMail(header, body);
+    } else {
+        return postMessage(sub, header, body);
+    }
+}
+
 // Add a new message to 'sub' in reply to parent message 'pid'
 function postReply (sub, body, pid) {
     var ret = false;
-- 
GitLab


From afb5d4c5707f2994860e18057f8d280517eefeca Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 00:58:47 -0500
Subject: [PATCH 049/752] Code style; typing.

---
 web/root/pages/001-forum.ssjs | 120 ++++++++++++++++++----------------
 1 file changed, 65 insertions(+), 55 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 2c54c60743..63562dc48c 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -1,16 +1,14 @@
 //Forum
+if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 
-if(typeof argv[0] != "boolean" || !argv[0])
-	exit();
-
-load("sbbsdefs.js");
-load("msgutils.js");
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "forum.js");
+load('sbbsdefs.js');
+load('msgutils.js');
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'forum.js');
 
 writeln('<script type="text/javascript" src="./js/forum.js"></script>');
 
-if(typeof http_request.query.notice != "undefined") {
+if (typeof http_request.query.notice !== 'undefined') {
 	writeln(
 		'<div id="noticebox" class="alert alert-warning">' + 
 		http_request.query.notice[0] + '</div>' +
@@ -21,23 +19,20 @@ if(typeof http_request.query.notice != "undefined") {
 }
 
 
-if(	typeof http_request.query.sub != "undefined"
-	&&
-	typeof msg_area.sub[http_request.query.sub[0]] != "undefined"
-	&&
-	typeof http_request.query.thread != "undefined"
+if (typeof http_request.query.sub !== 'undefined' &&
+	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined' &&
+	typeof http_request.query.thread !== 'undefined'
 ) {
 
 	// Thread view
 	var msgBase = new MsgBase(http_request.query.sub[0]);
 
-	var firstUnreadLi = "";
+	var firstUnreadLi = '';
 
-	var writeMessage = function(header, index) {
+	function writeMessage(header, index) {
 
 		var body = msgBase.get_msg_body(header.number);
-		if(body === null)
-			return;
+		if (body === null) return;
 
 		writeln(
 			'<li class="list-group-item striped' +
@@ -46,20 +41,23 @@ if(	typeof http_request.query.sub != "undefined"
 		);
 
 		// Show subject if first message
-		if(index == 0)
+		if (index === 0) {
 			writeln('<h4><strong>' + header.subject + '</strong></h4>');
+		}
 
 		// Header
-		if(user.alias != settings.guest && header.number > msg_area.sub[http_request.query.sub[0]].scan_ptr) {
-			writeln('<span title="Unread" class="glyphicon glyphicon-star"></span>');
-			if(firstUnreadLi == "")
-				firstUnreadLi = "li-" + header.number;
+		if (user.alias != settings.guest &&
+			header.number > msg_area.sub[http_request.query.sub[0]].scan_ptr
+		) {
+			writeln(
+				'<span title="Unread" class="glyphicon glyphicon-star"></span>'
+			);
+			if (firstUnreadLi === '') firstUnreadLi = 'li-' + header.number;
 		}			
 		writeln(
 			'From <strong>' + header.from + "</strong>" +
-			(header.from_net_type == 0 ? " " : "@" +  header.from_net_addr + " ") +
-			'to <strong>' + header.to + '</strong> ' +
-			'on ' +
+			(header.from_net_type == 0 ? '' : ('@' +  header.from_net_addr)) +
+			' to <strong>' + header.to + '</strong> on ' +
 			(new Date(header.when_written_time * 1000)).toLocaleString()
 		);
 
@@ -72,7 +70,8 @@ if(	typeof http_request.query.sub != "undefined"
 		// Standard controls
 		writeln(
 			'<a class="btn btn-default icon" title="Jump to oldest message" ' +
-			'aria-label="Jump to oldest message" href="#' + header.ec_thread.messages[0].number + '">' +
+			'aria-label="Jump to oldest message" href="#' +
+			header.ec_thread.messages[0].number + '">' +
 			'<span class="glyphicon glyphicon-fast-backward"></span></a>' +
 
 			'<a class="btn btn-default icon" title="Direct link to this message" ' +
@@ -85,7 +84,9 @@ if(	typeof http_request.query.sub != "undefined"
 		);
 
 		// User can post
-		if(user.alias != settings.guest && msg_area.sub[msgBase.cfg.code].can_post) {
+		if (user.alias !== settings.guest &&
+			msg_area.sub[msgBase.cfg.code].can_post
+		) {
 			writeln(
 				'<button class="btn btn-default icon" ' +
 				'aria-label="Reply to this message" ' +
@@ -98,9 +99,14 @@ if(	typeof http_request.query.sub != "undefined"
 		}
 
 		// User is operator
-		if(user.alias != settings.guest && msg_area.sub[msgBase.cfg.code].is_operator) {
+		if (user.alias != settings.guest &&
+			msg_area.sub[msgBase.cfg.code].is_operator
+		) {
 			writeln(
-				'<button class="btn btn-default icon" aria-label="Delete this message" title="Delete this message" onclick="deleteMessage(\'' + msgBase.cfg.code + '\', ' + header.number + ')" href="#">' +
+				'<button class="btn btn-default icon" ' +
+				'aria-label="Delete this message" title="Delete this message" ' +
+				'onclick="deleteMessage(\'' + msgBase.cfg.code + '\', ' + header.number + ')" ' +
+				'href="#">' +
 				'<span class="glyphicon glyphicon-trash"></span>' +
 				'</button>'
 			);
@@ -144,7 +150,7 @@ if(	typeof http_request.query.sub != "undefined"
 		messages.forEach(writeMessage);
 		writeln('</ul>');
 		msgBase.close();
-		if(messages.length > 1 && firstUnreadLi != "") {
+		if (messages.length > 1 && firstUnreadLi !== '') {
 			writeln(
 				'<script type="text/javascript">' +
 				'$("#jump-unread").attr("href", "#' + firstUnreadLi + '");' +
@@ -153,24 +159,25 @@ if(	typeof http_request.query.sub != "undefined"
 			);
 		}
 		// Update scan pointer
-		if(messages[messages.length - 1].number > msg_area.sub[http_request.query.sub[0]].scan_ptr)
+		if (messages[messages.length - 1].number > msg_area.sub[http_request.query.sub[0]].scan_ptr) {
 			msg_area.sub[http_request.query.sub[0]].scan_ptr = messages[messages.length - 1].number;
-	} catch(err) {
+		}
+	} catch (err) {
 		log(err);
 	}
 
-} else if(		
-	typeof http_request.query.sub != "undefined"
-	&&
-	typeof msg_area.sub[http_request.query.sub[0]] != "undefined"
+} else if (
+	typeof http_request.query.sub !== 'undefined' &&
+	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined'
 ) {
 
 	// Thread list
-	var writeThread = function(thread) {
-		if(user.number > 0 && user.alias != settings.guest)
+	function writeThread(thread) {
+		if (user.number > 0 && user.alias != settings.guest) {
 			var unread = getUnreadInThread(http_request.query.sub[0], thread);
-		else
+		} else {
 			var unread = 0;
+		}
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" class="list-group-item striped">' +
@@ -215,7 +222,9 @@ if(	typeof http_request.query.sub != "undefined"
 		)
 	);
 
-	if(user.alias != settings.guest && msg_area.sub[http_request.query.sub[0]].can_post) {
+	if (user.alias !== settings.guest &&
+		msg_area.sub[http_request.query.sub[0]].can_post
+	) {
 		writeln(
 			'<button class="btn btn-default icon" ' +
 			'aria-label="Post a new message" title="Post a new message" ' +
@@ -225,7 +234,7 @@ if(	typeof http_request.query.sub != "undefined"
 		);
 	}
 
-	if(user.alias != settings.guest) {
+	if (user.alias !== settings.guest) {
 		writeln(
 			'<button id="scan-cfg-new" class="btn ' +
 			(!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) && (msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) ? "btn-primary" : "btn-default") +
@@ -260,18 +269,17 @@ if(	typeof http_request.query.sub != "undefined"
 		writeln('<div id="forum-list-container" class="list-group">');
 		threads.order.forEach(function(t){writeThread(threads.thread[t]);});
 		writeln('</div>');
-	} catch(err) {
+	} catch (err) {
 		log(err);
 	}
 
-} else if(
-	typeof http_request.query.group != "undefined"
-	&&
-	typeof msg_area.grp_list[http_request.query.group[0]] != "undefined"
+} else if (
+	typeof http_request.query.group !== 'undefined' &&
+	typeof msg_area.grp_list[http_request.query.group[0]] !== 'undefined'
 ) {
 
 	// Sub list
-	var writeSub = function(sub) {
+	function writeSub(sub) {
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;sub=%s" class="list-group-item striped%s">' +
@@ -292,10 +300,10 @@ if(	typeof http_request.query.sub != "undefined"
 		);
 	}
 
-	var writeApiCall = function(subs) {
+	function writeApiCall(subs) {
 		writeln('<script type="text/javascript">');
 		var codes = [];
-		subs.forEach(function(sub) { codes.push(sub.code); });
+		subs.forEach(function (sub) { codes.push(sub.code); });
 		codes = codes.join('&sub=');
 		writeln('getSubUnreadCount("' + codes + '");');
 		writeln('setInterval(function(){getSubUnreadCount("' + codes + '");}, 60000);');
@@ -320,16 +328,17 @@ if(	typeof http_request.query.sub != "undefined"
 		writeln('<div id="forum-list-container" class="list-group">');
 		subs.forEach(writeSub);
 		writeln('</div>');
-		if(user.number > 0 && user.alias != settings.guest)
+		if (user.number > 0 && user.alias !== settings.guest) {
 			writeApiCall(subs);
-	} catch(err) {
+		}
+	} catch (err) {
 		log(err);
 	}
 
 } else {
 
 	// Group list
-	var writeGroup = function(group) {
+	function writeGroup(group) {
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;group=%s" class="list-group-item striped">' +
@@ -353,7 +362,7 @@ if(	typeof http_request.query.sub != "undefined"
 		);
 	}
 
-	var writeApiCall = function(groups) {
+	function writeApiCall(groups) {
 		writeln('<script type="text/javascript">');
 		var indexes = [];
 		groups.forEach(function(group) { indexes.push(group.index); });
@@ -376,9 +385,10 @@ if(	typeof http_request.query.sub != "undefined"
 		writeln('<div id="forum-list-container" class="list-group">');
 		groups.forEach(writeGroup);
 		writeln('</div>');
-		if(user.number > 0 && user.alias != settings.guest)
+		if (user.number > 0 && user.alias !== settings.guest) {
 			writeApiCall(groups);
-	} catch(err) {
+		}
+	} catch (err) {
 		log(err);
 	}
 
-- 
GitLab


From 9341c3e4ab137c0918a4d38cc485124a08c7d859 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 01:48:00 -0500
Subject: [PATCH 050/752] Be more efficient about fetching page titles.

---
 web/lib/pages.js | 42 ++++++++++++++++++++++++++----------------
 1 file changed, 26 insertions(+), 16 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 84f56f7e40..af86ec3f8b 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -46,7 +46,10 @@ function getPages() {
 			if (file_isdir(item)) return;
 			var fn = file_getname(item);
 			var title = getPageTitle(item);
-			if (typeof title === 'undefined' || title.search(/^HIDDEN/) === 0) {
+			if (typeof title === 'undefined' ||
+				title.search(/^HIDDEN/) === 0 ||
+				fn.search(/\.xjs\.ssjs$/i) >= 0
+			) {
 				return;
 			}
 			pages.push(
@@ -63,29 +66,36 @@ function getPages() {
 
 function getPageTitle(file) {
 
+	var title;
+
 	var ext = file_getext(file).toUpperCase();
 
 	var f = new File(file);
-	f.open('r');
-	var i = f.readAll();
-	f.close();
 
-	if (ext == '.JS' || (ext == '.SSJS' && file.search(/\.xjs\.ssjs$/i)==-1)) {
-		var title = i[0].replace(/\/\//g, '');
-		return title;
-	}
-
-	if (ext === '.HTML' || ext === '.XJS') {
+	if (ext === '.JS' ||
+		(ext === '.SSJS' && file.search(/\.xjs\.ssjs$/i) === -1)
+	) {
+		f.open('r');
+		var i = f.readln();
+		f.close();
+		title = i.replace(/\/\//g, '');
+	} else if (ext === '.HTML' || ext === '.XJS') {
 		// Seek first comment line in an HTML document
-		for (var j = 0; j < i.length; j++) {
-			var k = i[j].match(/^\<\!\-\-.*\-\-\>$/);
-			if (k === null) continue;
-			var title = k[0].replace(/[\<\!\-+|\-+\>]/g, '');
-			return title;
+		f.open('r');
+		while (!f.eof) {
+			var i = f.readln();
+			var k = i.match(/^\<\!\-\-.*\-\-\>$/);
+			if (k !== null) {
+				title = k[0].replace(/[\<\!\-+|\-+\>]/g, '');
+				break;
+			}
 		}
+		f.close();
+	} else if (ext === '.TXT') {
+		title = file_getname(file);
 	}
 
-	if (ext == '.TXT') return file_getname(file);
+	return title;
 
 }
 
-- 
GitLab


From 8c28faa7bcfa649b11419f452afaa24420e493bf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 01:48:44 -0500
Subject: [PATCH 051/752] Be more efficient about fetching page titles.

---
 web/lib/pages.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index af86ec3f8b..6a5721dcdf 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -67,11 +67,8 @@ function getPages() {
 function getPageTitle(file) {
 
 	var title;
-
 	var ext = file_getext(file).toUpperCase();
-
 	var f = new File(file);
-
 	if (ext === '.JS' ||
 		(ext === '.SSJS' && file.search(/\.xjs\.ssjs$/i) === -1)
 	) {
-- 
GitLab


From 9bff5a14e1a0c19d4071258ce7ae183465db9e18 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 08:34:38 -0500
Subject: [PATCH 052/752] Allow for a title in HIDDEN pages

---
 web/lib/pages.js   |  8 +++++++-
 web/root/index.xjs | 22 +++++++++++++++-------
 2 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 6a5721dcdf..b357991efc 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -108,7 +108,13 @@ function getPage(page) {
 
 	if (user.alias != settings.guest) {
 		var title = getPageTitle(page);
-		if (title != 'HIDDEN') setSessionValue(user.number, 'action', title);
+		if (title != 'HIDDEN') {
+			setSessionValue(
+				user.number,
+				'action',
+				title.replace(/^HIDDEN(\:)+/, '')
+			);
+		}
 	}
 
 	switch(ext) {
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 0751b2fa8c..adf9d43b68 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -25,11 +25,15 @@
 		<meta charset="utf-8">
 		<meta http-equiv="X-UA-Compatible" content="IE=edge">
 		<meta name="viewport" content="width=device-width, initial-scale=1">
-		<meta name="description" content="">
-		<meta name="author" content="">
 		<link rel="icon" href="./images/favicon.ico">
 		<title>
-			<?xjs write(getPageTitle(settings.web_root + 'pages/' + page)); ?>
+			<?xjs 
+				write(
+					getPageTitle(
+						settings.web_root + 'pages/' + page
+					).replace(/^HIDDEN(\:)*/, '')
+				);
+			?>
 			:
 			<?xjs write(system.name); ?>
 		</title>
@@ -48,7 +52,9 @@
 			<div class="modal-dialog" role="document">
 				<div class="modal-content">
 					<div class="modal-header">
-						<h4 class="modal-title" id="popUpModalTitle">Pop-Up Thingie</h4>
+						<h4 class="modal-title" id="popUpModalTitle">
+							Pop-Up Thingie
+						</h4>
 					</div>
 					<div class="modal-body" id="popUpModalBody"></div>
 					<div class="modal-footer">
@@ -132,7 +138,11 @@
 					<?xjs
 					if (user.alias === settings.guest || user.number < 1) {
 						if (settings.user_registration) {
-							writeln('<li><a href="./?page=000-register.xjs">Register</a></li>');
+							writeln(
+								'<li>' +
+								'<a href="./?page=000-register.xjs">Register</a>' +
+								'</li>'
+							);
 						}
 						writeln(
 							'<li class="dropdown">' +
@@ -192,7 +202,6 @@
 							<span class="glyphicon glyphicon-tasks"></span> Sidebar
 						</button>
 					</p>
-					<div class="row">
 					<?xjs 
 						var ini = getWebCtrl();
 						if ((typeof ini == "boolean" && !ini) ||
@@ -201,7 +210,6 @@
 							write(getPage(page));
 						}
 					?>
-					</div>
 				</div>
 
 				<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
-- 
GitLab


From e8a9989b6e2d79f1779e50db20124a25979e3882 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 08:35:23 -0500
Subject: [PATCH 053/752] Added page title

---
 web/root/pages/000-mail.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/000-mail.ssjs b/web/root/pages/000-mail.ssjs
index 40eabd8f2f..7c62e4a1b0 100644
--- a/web/root/pages/000-mail.ssjs
+++ b/web/root/pages/000-mail.ssjs
@@ -1,4 +1,4 @@
-//HIDDEN
+//HIDDEN:Mail
 
 if(typeof argv[0] != "boolean" || !argv[0])
 	exit();
-- 
GitLab


From 70d4718c10bac11b0b2669a369a4755bdedefe46 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 08:37:34 -0500
Subject: [PATCH 054/752] Added page title

---
 web/root/pages/000-register.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
index 7f8c2496df..917482cde7 100644
--- a/web/root/pages/000-register.xjs
+++ b/web/root/pages/000-register.xjs
@@ -1,4 +1,4 @@
-<!--HIDDEN-->
+<!--HIDDEN:Registration-->
 
 <!--
 	Username/alias and password are always required. All other fields are set
-- 
GitLab


From 7580c12d22043c599846561cfac7f74b261b531f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Dec 2015 08:39:55 -0500
Subject: [PATCH 055/752] Get rid of the loading icon for now

---
 web/root/pages/001-forum.ssjs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 63562dc48c..c403c6b5cd 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -285,7 +285,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				'<a href="./?page=%s&amp;sub=%s" class="list-group-item striped%s">' +
 				'<h4><strong>%s</strong></h4>' +
 				'<span title="Unread messages" class="badge %s" id="badge-%s">' +
-				'<img src="./images/ajax-loader-small.gif">' +
+				//'<img src="./images/ajax-loader-small.gif">' +
 				'</span>' +
 				'<p>%s</p>' +
 				'</a>',
@@ -344,10 +344,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				'<a href="./?page=%s&amp;group=%s" class="list-group-item striped">' +
 				'<h3><strong>%s</strong></h3>' +
 				'<span title="Unread messages (other)" class="badge ignored" id="badge-ignored-%s">' +
-				'<img src="./images/ajax-loader-small.gif">' +
+				//'<img src="./images/ajax-loader-small.gif">' +
 				'</span>' +
 				'<span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-%s">' +
-				'<img src="./images/ajax-loader-small.gif">' +
+				//'<img src="./images/ajax-loader-small.gif">' +
 				'</span>' +
 				'<p>%s : %s sub-boards</p>' +
 				'</a>',
-- 
GitLab


From 20ce2df05f473da1f5c2015c57bac4b65d8bc26b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 00:21:29 -0500
Subject: [PATCH 056/752] Cleanup

---
 web/root/api/attachments.ssjs |  61 ++++----
 web/root/api/auth.ssjs        |   6 +-
 web/root/api/files.ssjs       |   2 +-
 web/root/api/register.ssjs    | 252 +++++++++++++++++++---------------
 web/root/api/system.ssjs      | 105 +++++++-------
 5 files changed, 225 insertions(+), 201 deletions(-)

diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index 4d8f4eb0ca..51cf998ab8 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -1,58 +1,59 @@
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "auth.js");
-load(settings.web_lib + "mime-decode.js");
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'mime-decode.js');
 
-var barfOut = function(err) {
+function barfOut(err) {
 	log(err);
 	exit();
 }
 
-if(	typeof http_request.query.sub == "undefined"
-	||
-	(	http_request.query.sub[0] != 'mail'
-		&&
-		typeof msg_area.sub[http_request.query.sub[0]] == "undefined"
+if (typeof http_request.query.sub === 'undefined' ||
+	(	http_request.query.sub[0] !== 'mail' &&
+		typeof msg_area.sub[http_request.query.sub[0]] === 'undefined'
 	)
 ) {
-	barfOut("Invalid sub.");
+	barfOut('Invalid sub.');
 }
 
 var sub = http_request.query.sub[0];
 
-if(typeof http_request.query.msg == "undefined")
-	barfOut("No message number provided.");
+if (typeof http_request.query.msg === 'undefined') {
+	barfOut('No message number provided.');
+}
+
 var id = parseInt(http_request.query.msg[0]);
 
-if(typeof http_request.query.cid != "undefined")
+if (typeof http_request.query.cid !== 'undefined') {
 	var cid = http_request.query.cid[0];
-else if(typeof http_request.query.filename != "undefined")
+} else if (typeof http_request.query.filename !== 'undefined') {
 	var filename = http_request.query.filename[0];
-else
-	barfOut("No attachment specified.");
+} else {
+	barfOut('No attachment specified.');
+}
 
 var msgBase = new MsgBase(sub);
-if(!msgBase.open())
-	barfOut("Unable to open MsgBase " + sub);
+if (!msgBase.open()) barfOut('Unable to open MsgBase ' + sub);
 
 var header = msgBase.get_msg_header(false, id);
-if(header === null)
-	barfOut("No such message.");
-if(typeof msgBase.cfg == "undefined" && header.to_ext != user.number)
-	barfOut("Not your message.");
+if (header === null) barfOut('No such message.');
+if (typeof msgBase.cfg === 'undefined' && header.to_ext != user.number) {
+	barfOut('Not your message.');
+}
 
 var body = msgBase.get_msg_body(false, id, header);
-if(body === null)
-	barfOut("Cannot read message body!");
+if (body === null) barfOut('Cannot read message body!');
 msgBase.close();
 
-if(typeof cid != "undefined")
+if (typeof cid !== 'undefined') {
 	var att = mime_get_cid_attach(header, body, cid);
-else if(typeof filename != "undefined")
+} else if (typeof filename !== 'undefined') {
 	var att = mime_get_attach(header, body, filename);
+}
 
-if(typeof att != "undefined") {
-	if(typeof att.content_type != "undefined")
-		http_reply.header["Content-Type"] = att.content_type;
-	http_reply.header["Content-Length"] = att.body.length;
+if (typeof att != 'undefined') {
+	if (typeof att.content_type !== 'undefined') {
+		http_reply.header['Content-Type'] = att.content_type;
+	}
+	http_reply.header['Content-Length'] = att.body.length;
 	write(att.body);
 }
\ No newline at end of file
diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 7ca3eab841..7919e8ac92 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -2,10 +2,10 @@ load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'auth.js');
 
 var response = JSON.stringify(
-	{ 'authenticated' : (user.alias !== settings.guest) }
+	{ authenticated : (user.alias !== settings.guest) }
 );
 
-http_reply.header["Content-Type"] = "application/json";
-http_reply.header["Content-Length"] = response.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = response.length;
 
 write(response);
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 7e3125a215..f2c37f1413 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -5,7 +5,7 @@ load('filedir.js');
 
 var reply = {};
 
-if ((http_request.method === "GET" || http_request.method === "POST") &&
+if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	typeof http_request.query.call !== 'undefined' &&
 	user.number > 0	&&
 	user.alias !== settings.guest
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 1d8073f6f4..992c4d587d 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,12 +1,9 @@
 load('sbbsdefs.js');
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "/auth.js");
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + '/auth.js');
 
-if(user.alias != settings.guest)
-	exit();
-
-if(!settings.user_registration)
-	exit();
+if (user.alias !== settings.guest) exit();
+if (!settings.user_registration) exit();
 
 var MIN_ALIAS = 1,
 	MIN_REALNAME = 3,
@@ -17,164 +14,199 @@ var MIN_ALIAS = 1,
 
 
 var reply = {
-	'errors' : [],
-	'userNumber' : 0
+	errors : [],
+	userNumber : 0
 };
 
 var prepUser = {
-	'alias' : "",
-	'handle' : "",
-	'realname' : "",
-	'netmail' : "",
-	'address' : "",
-	'location' : "",
-	'phone' : "",
-	'birthdate' : "",
-	'gender' : "",
-	'password' : ""
+	alias : '',
+	handle : '',
+	realname : '',
+	netmail : '',
+	address : '',
+	location : '',
+	phone : '',
+	birthdate : '',
+	gender : '',
+	password : ''
 };
 
-var required = function(mask) {
+function required(mask) {
 	return (system.new_user_questions&mask);
 }
 
-var cleanParam = function(param) {
-	if(paramExists(param))
-		return http_request.query[param][0].replace(/[^\x20-\x7E]/g, "");
+function cleanParam(param) {
+	if (paramExists(param)) {
+		return http_request.query[param][0].replace(/[^\x20-\x7E]/g, '');
+	}
 	return "";
 }
 
-var paramExists = function(param) {
-	if(	typeof http_request.query[param] != "undefined"
-		&&
-		http_request.query[param][0] != ""
+function paramExists(param) {
+	if (typeof http_request.query[param] !== 'undefined' &&
+		http_request.query[param][0] !== ''
 	) {
 		return true;
 	}
 	return false;
 }
 
-var paramLength = function(param) {
-	if(typeof http_request.query[param] == "undefined")
+function paramLength(param) {
+	if (typeof http_request.query[param] === 'undefined') {
 		return 0;
-	else if(http_request.query[param][0].replace(" ", "").length < 1)
+	} else if (http_request.query[param][0].replace(' ', '').length < 1) {
 		return 0;
-	else if(cleanParam(param).length < 1)
+	} else if (cleanParam(param).length < 1) {
 		return 0;
-	else
+	} else {
 		return http_request.query[param][0].length;
+	}
 }
 
-var newUser = function() {
+function newUser() {
 	var usr = system.new_user(prepUser.alias);
-	if(typeof usr == "number") {
-		reply.errors.push("Failed to create user record.");
+	if (typeof usr === 'number') {
+		reply.errors.push('Failed to create user record.');
 		return;
 	}
-	log("User #" + usr.number + " registered via HTTP.");
+	log('User #' + usr.number + ' registered via HTTP.');
 	usr.security.password = prepUser.password;
-	for(var property in prepUser) {
-		if(property == "alias" || property == "password")
-			continue;
+	for (var property in prepUser) {
+		if (property === 'alias' || property === 'password') continue;
 		usr[property] = prepUser[property];
 	}
 	reply.userNumber = usr.number;
 }
 
 // See if the hidden form fields were filled
-if(	(	paramExists("send-me-free-stuff")
-		&&
-		http_request.query["send-me-free-stuff"][0] != ""
-	)
-	||
-	(	paramExists("subscribe-to-newsletter")
-		&&
-		http_request.query["subscribe-to-newsletter"][0] != ""
+if ((	paramExists('send-me-free-stuff') &&
+		http_request.query['send-me-free-stuff'][0] !== ''
+	) ||
+	(	paramExists('subscribe-to-newsletter') &&
+		http_request.query['subscribe-to-newsletter'][0] !== ''
 	)
 ) {
-	log("Hidden registration form input element filled.  Likely a bot.  Cancelling user registration.");
+	log('Hidden registration form input element filled.  ' +
+		'Likely a bot.  Cancelling user registration.'
+	);
 	exit();
 }
 
-if(	system.newuser_password != ""
-	&&
-	(	typeof http_request.query["newuser-password"] == "undefined"
-		||
-		http_request.query["newuser-password"][0] != system.newuser_password
+if (system.newuser_password !== '' &&
+	(	typeof http_request.query['newuser-password'] === 'undefined' ||
+		http_request.query['newuser-password'][0] != system.newuser_password
 	)
 ) {
-	reply.errors.push("Incorrect registration password.");
+	reply.errors.push('Incorrect registration password.');
 }
 
 // More could be done to respect certain newuser question toggles
 // (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
 
-if(!paramExists("alias") || paramLength("alias") < MIN_ALIAS || paramLength("alias") > LEN_ALIAS) {
-	reply.errors.push("Valid username is required.");
-} else if(system.matchuser(http_request.query.alias[0]) > 0) {
-	reply.errors.push("Username already taken.");
+if (!paramExists('alias') ||
+	paramLength('alias') < MIN_ALIAS ||
+	paramLength('alias') > LEN_ALIAS
+) {
+	reply.errors.push('Valid username is required.');
+} else if (system.matchuser(http_request.query.alias[0]) > 0) {
+	reply.errors.push('Username already taken.');
+} else {
+	prepUser.alias = cleanParam('alias');
+	prepUser.handle = cleanParam('alias');
+}
+
+if ((!paramExists('password1') || !paramExists('password2')) ||
+	http_request.query.password1[0] !== http_request.query.password2[0]
+) {
+	reply.errors.push('Password & confirmation are required, and must match.');
+} else if (
+	paramLength('password1') < settings.minimum_password_length ||
+	paramLength('password1') > LEN_PASS
+) {
+	reply.errors.push(
+		'Password must be between ' +
+		settings.minimum_password_length + ' and ' + LEN_PASS + ' in length.'
+	);
+} else {
+	prepUser.password = cleanParam('password1');
+}
+
+if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
+	reply.errors.push('Email address is required.');
+} else if (
+	(	paramLength('netmail') < MIN_NETMAIL ||
+		paramLength('netmail') > LEN_NETMAIL
+	) && !required(UQ_NONETMAIL)
+) {
+	reply.errors.push('Invalid email address.');
+} else {
+	prepUser.netmail = cleanParam('netmail');
+}
+
+if (required(UQ_REALNAME) &&
+	(	!paramExists('realname') ||
+		paramLength('realname') < MIN_REALNAME ||
+		paramLength('realname') > LEN_NAME
+	)
+) {
+	reply.errors.push('Valid real name is required.');
+} else {
+	prepUser.realname = cleanParam('realname');
+}
+
+if (required(UQ_LOCATION) &&
+	(	!paramExists('location') ||
+		paramLength('location') < MIN_LOCATION ||
+		paramLength('location') > LEN_LOCATION
+	)
+) {
+	reply.errors.push('Valid location is required.');
 } else {
-	prepUser.alias = cleanParam("alias");
-	prepUser.handle = cleanParam("alias");
+	prepUser.location = cleanParam('location');
 }
 
-if(	(!paramExists("password1") || !paramExists("password2"))
-	||
-	http_request.query.password1[0] != http_request.query.password2[0]
+if (required(UQ_ADDRESS) &&
+	(	!paramExists('address') ||
+		paramLength('address') < MIN_ADDRESS ||
+		paramLength('address') > LEN_ADDRESS
+	)
 ) {
-	reply.errors.push("Password & confirmation are required, and must match.");
-} else if(paramLength("password1") < settings.minimum_password_length || paramLength("password1") > LEN_PASS) {
-	reply.errors.push("Password must be between " + settings.minimum_password_length + " and " + LEN_PASS + " in length.");
+	reply.errors.push('Valid street address is required.');
 } else {
-	prepUser.password = cleanParam("password1");
-}
-
-if(!paramExists("netmail") && !required(UQ_NONETMAIL))
-	reply.errors.push("Email address is required.");
-else if((paramLength("netmail") < MIN_NETMAIL || paramLength("netmail") > LEN_NETMAIL) && !required(UQ_NONETMAIL))
-	reply.errors.push("Invalid email address.");
-else
-	prepUser.netmail = cleanParam("netmail");
-
-if(required(UQ_REALNAME) && (!paramExists("realname") || paramLength("realname") < MIN_REALNAME || paramLength("realname") > LEN_NAME))
-	reply.errors.push("Valid real name is required.");
-else
-	prepUser.realname = cleanParam("realname");
-
-if(required(UQ_LOCATION) && (!paramExists("location") || paramLength("location") < MIN_LOCATION || paramLength("location") > LEN_LOCATION))
-	reply.errors.push("Valid location is required.");
-else
-	prepUser.location = cleanParam("location");
-
-if(required(UQ_ADDRESS) && (!paramExists("address") || paramLength("address") < MIN_ADDRESS || paramLength("address") > LEN_ADDRESS))
-	reply.errors.push("Valid street address is required.");
-else
-	prepUser.address = cleanParam("address");
-
-if(required(UQ_PHONE) && (!paramExists("phone") || paramLength("phone") < MIN_PHONE || paramLength("phone") > LEN_PHONE))
-	reply.errors.push("Valid phone number is required.");
-else
-	prepUser.phone = cleanParam("phone");
-
-if(required(UQ_SEX) && (!paramExists("gender") || paramLength("gender") != 1))
-	reply.errors.push("Sex is required. Heh heh.");
-else
-	prepUser.gender = cleanParam("gender");
-
-if(	paramExists("birth")
-	&&
+	prepUser.address = cleanParam('address');
+}
+
+if (required(UQ_PHONE) &&
+	(	!paramExists('phone') ||
+		paramLength('phone') < MIN_PHONE ||
+		paramLength('phone') > LEN_PHONE
+	)
+) {
+	reply.errors.push('Valid phone number is required.');
+} else {
+	prepUser.phone = cleanParam('phone');
+}
+
+if (required(UQ_SEX) &&
+	(!paramExists('gender') || paramLength('gender') != 1)
+) {
+	reply.errors.push('Sex is required. Heh heh.');
+} else {
+	prepUser.gender = cleanParam('gender');
+}
+
+if (paramExists('birth') &&
 	http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
 ) {
 	// Should really test for valid date (and date format per system config)
-	prepUser.birthdate = cleanParam("birth");
-} else if(required(UQ_BIRTH)) {
-	reply.errors.push("Birthdate is required.");
+	prepUser.birthdate = cleanParam('birth');
+} else if (required(UQ_BIRTH)) {
+	reply.errors.push('Birthdate is required.');
 }
 
-if(reply.errors.length < 1)
-	newUser();
+if (reply.errors.length < 1) newUser();
 
 reply = JSON.stringify(reply);
-http_reply.header["Content-Type"] = "application/json";
-http_reply.header["Content-Length"] = reply.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
 write(reply);
\ No newline at end of file
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index e40d9f0849..c7cbdaab76 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -1,90 +1,81 @@
-load("sbbsdefs.js");
-load("nodedefs.js");
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "auth.js");
+load('sbbsdefs.js');
+load('nodedefs.js');
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'auth.js');
 
 var reply = {};
 
-if(	(http_request.method == "GET" || http_request.method == "POST")
-	&&
-	typeof http_request.query.call != "undefined"
-	&&
+if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+	typeof http_request.query.call !== 'undefined' &&
 	user.number > 0
 ) {
 
-	switch(http_request.query.call[0]) {
+	switch (http_request.query.call[0]) {
 
-		case "node-list":
+		case 'node-list':
 			reply = system.node_list.map(
-				function(node) {
-					if(node.status == 3)
-						var usr = new User(node.useron);
+				function (node) {
+					if (node.status === 3) var usr = new User(node.useron);
 					return ({
-						'status' : NodeStatus[node.status],
-						'action' : NodeAction[node.action],
-						'user' : (typeof usr == "undefined" ? "" : usr.alias)
+						status : NodeStatus[node.status],
+						action : NodeAction[node.action],
+						user : (typeof usr === 'undefined' ? '' : usr.alias)
 					});
 				}
 			);
 			var usr = new User(1);
-			for(var un = 1; un < system.lastuser; un++) {
+			for (var un = 1; un < system.lastuser; un++) {
 				usr.number = un;
-				if(usr.connection != "HTTP")
-					continue;
-				if(usr.alias == settings.guest)
-					continue;
-				if(usr.settings&USER_QUIET)
-					continue;
-				if(usr.logontime < time() - settings.inactivity)
-					continue;
-				var webAction = getSessionValue(usr.number, "action");
-				if(webAction === null)
-					continue;
+				if (usr.connection !== 'HTTP') continue;
+				if (usr.alias === settings.guest) continue;
+				if (usr.settings&USER_QUIET) continue;
+				if (usr.logontime < time() - settings.inactivity) continue;
+				var webAction = getSessionValue(usr.number, 'action');
+				if (webAction === null) continue;
 				reply.push(
-					{	'status' : "",
-						'action' : "viewing " + webAction,
-						'user' : usr.alias
+					{	status : '',
+						action : 'viewing ' + webAction,
+						user : usr.alias
 					}
 				);
 			}
 			break;
 
-		case "send-telegram":
-			if(user.alias == settings.guest)
+		case 'send-telegram':
+			if (user.alias === settings.guest) break;
+			if (typeof http_request.query.user === 'undefined') break;
+			if (typeof http_request.query.telegram === 'undefined' ||
+				http_request.query.telegram[0] === ''
+			) {
 				break;
-			if(typeof http_request.query.user == "undefined")
-				break;
-			if(typeof http_request.query.telegram == "undefined" || http_request.query.telegram[0] == "")
-				break;
-			if(http_request.query.telegram[0].length > settings.maximum_telegram_length)
+			}
+			if (http_request.query.telegram[0].length >
+				settings.maximum_telegram_length
+			) {
 				break;
+			}
 			var un = system.matchuser(http_request.query.user[0]);
-			if(un < 1)
-				break;
+			if (un < 1) break;
 			system.put_telegram(
 				un,
-				"Telegram from " +
-				user.alias + " via WWW on " + system.timestr() + "\r\n" +
-				http_request.query.telegram[0] +
-				"\r\n"
+				'Telegram from ' +
+				user.alias + ' via WWW on ' + system.timestr() + '\r\n' +
+				http_request.query.telegram[0] + '\r\n'
 			);
 			break;
 
-		case "get-telegram":
-			if(user.alias == settings.guest)
-				break;
+		case 'get-telegram':
+			if (user.alias === settings.guest) break;
 			reply.telegram = system.get_telegram(user.number);
 			break;
 
-		case "set-xtrn-intent":
-			if(user.alias == settings.guest)
-				break;
-			if(typeof http_request.query.code == "undefined")
-				break;
-			if(http_request.query.code[0].length > 8)
-				break;
-			if(typeof xtrn_area.prog[http_request.query.code[0]] == "undefined")
+		case 'set-xtrn-intent':
+			if (user.alias === settings.guest) break;
+			if (typeof http_request.query.code === 'undefined') break;
+			if (http_request.query.code[0].length > 8) break;
+			if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
 				break;
+			}
 			setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
 			break;
 
@@ -96,6 +87,6 @@ if(	(http_request.method == "GET" || http_request.method == "POST")
 }
 
 reply = JSON.stringify(reply);
-http_reply.header["Content-Type"] = "application/json";
-http_reply.header["Content-Length"] = reply.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
 write(reply);
\ No newline at end of file
-- 
GitLab


From 0b2a0778abb3b1342b5b7c874ad3a39a37cea9a2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 00:53:53 -0500
Subject: [PATCH 057/752] Cleanup

---
 web/root/js/common.js |   4 +-
 web/root/js/forum.js  | 181 +++++++++++++++++++++++-------------------
 2 files changed, 100 insertions(+), 85 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index ba1a408772..8806b7ce31 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -12,8 +12,8 @@ function login(evt) {
 		{	'url' : './api/auth.ssjs',
 			'method' : 'POST',
 			'data' : {
-				'username' : $('#input-username').val(),
-				'password' : $('#input-password').val()
+				username : $('#input-username').val(),
+				password : $('#input-password').val()
 			}
 		}
 	).done(
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index cc96de88c8..7a3eb90415 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -1,108 +1,106 @@
 // Add a parameter to the query string
-var insertParam = function(key, value) {
+function insertParam(key, value) {
     key = encodeURIComponent(key);
     value = encodeURIComponent(value);
     var kvp = window.location.search.substr(1).split('&');
     var i = kvp.length,	x;
-    while(i--) {
+    while (i--) {
         x = kvp[i].split('=');
-        if (x[0]==key) {
+        if (x[0] == key) {
             x[1] = value;
             kvp[i] = x.join('=');
             break;
         }
     }
-    if(i<0)
-    	kvp[kvp.length] = [key,value].join('=');
+    if (i<0) kvp[kvp.length] = [key,value].join('=');
     window.location.search = kvp.join('&'); 
 }
 
 // For now we'll just remove nested quotes from the parent post
-var quotify = function(id) {
-	$('#quote-' + id).attr("disabled", true);
+function quotify(id) {
+	$('#quote-' + id).attr('disabled', true);
 	var html = $('#message-' + id).clone();
 	html.find('blockquote').remove();
 	$('#replytext-' + id).val(
 		html.text().replace(/\n\s*\n\s*\n/g, '\n\n').split(/\r?\n/).map(
-			function(line) { return ("> " + line); }
-		).join("\n") +
+			function (line) { return ("> " + line); }
+		).join('\n') +
 		$('#replytext-' +id).val()
 	);
 }
 
 // (Try to) post a new message to 'sub' via the web API
-var postNew = function(sub) {
-	$('#newmessage-button').attr("disabled", true);
+function postNew(sub) {
+	$('#newmessage-button').attr('disabled', true);
 	var to = $('#newmessage-to').val();
 	var subject = $('#newmessage-subject').val();
 	var body = $('#newmessage-body').val();
 	$.ajax(
-		{	'url' : "./api/forum.ssjs",
-			'method' : "POST",
-			'data' : {
-				'call' : "post",
-				'sub' : sub,
-				'to' : to,
-				'subject' : subject,
-				'body' : body
+		{	url : './api/forum.ssjs',
+			method : 'POST',
+			data : {
+				call : 'post',
+				sub : sub,
+				to : to,
+				subject : subject,
+				body : body
 			}
 		}
 	).done(
-		function(data) {
-			if(data.success) {
+		function (data) {
+			if (data.success) {
 				$('#newmessage').remove();
-				insertParam("notice", "Your message has been posted.");
+				insertParam('notice', 'Your message has been posted.');
 			}
-			$('#newmessage-button').attr("disabled", false);
+			$('#newmessage-button').attr('disabled', false);
 		}
 	);
 }
 
 // (Try to) post a reply to message number 'id' of 'sub' via the web API
-var postReply = function(sub, id) {
-	$('#reply-button-' + id).attr("disabled", true);
+function postReply(sub, id) {
+	$('#reply-button-' + id).attr('disabled', true);
 	var body = $('#replytext-' + id).val();
 	$.ajax(
-		{	'url' : "./api/forum.ssjs",
-			'method' : "POST",
-			'data' : {
-				'call' : "post-reply",
-				'sub' : sub,
-				'body' : $('#replytext-' + id).val(),
-				'pid' : id
+		{	url : './api/forum.ssjs',
+			method : 'POST',
+			data : {
+				call : 'post-reply',
+				sub : sub,
+				body : $('#replytext-' + id).val(),
+				pid : id
 			}
 		}
 	).done(
-		function(data) {
-			if(data.success) {
-				$('#quote-' + id).attr("disabled", false);
+		function (data) {
+			if (data.success) {
+				$('#quote-' + id).attr('disabled', false);
 				$('#replybox-' + id).remove();
-				insertParam("notice", "Your message has been posted.");
+				insertParam('notice', 'Your message has been posted.');
 			} else {
-				$('#reply-button-' + id).attr("disabled", false);
+				$('#reply-button-' + id).attr('disabled', false);
 			}
 		}
 	);
 }
 
 // (Try to) delete a message via the web API
-var deleteMessage = function(sub, id) {
+function deleteMessage(sub, id) {
 	$.getJSON(
 		'./api/forum.ssjs?call=delete-message&sub=' + sub + '&number=' + id,
-		function(data) {
+		function (data) {
 			console.log(data);
-			if(data.success) {
+			if (data.success) {
 				$('#li-' + id).remove();
-				insertParam("notice", "Message deleted.");
+				insertParam('notice', 'Message deleted.');
 			}
 		}
 	);
 }
 
 // Add a new message input form to the element with id 'forum-list-container' for sub 'sub'
-var addNew = function(sub) {
-	if($('#newmessage').length > 0)
-		return;
+function addNew(sub) {
+	if ($('#newmessage').length > 0) return;
 	$('#forum-list-container').append(
 		'<li id="newmessage" class="list-group-item">' +
 		'<input id="newmessage-to" class="form-control" type="text" placeholder="To"><br>' +
@@ -113,17 +111,18 @@ var addNew = function(sub) {
 	);
 	$.getJSON(
 		'./api/forum.ssjs?call=get-signature',
-		function(data) {
-			$('#newmessage-body').val($('#newmessage-body').val() + "\r\n" + data.signature);
+		function (data) {
+			$('#newmessage-body').val(
+				$('#newmessage-body').val() + '\r\n' + data.signature
+			);
 		}
 	);
-	window.location.hash = "#newmessage";
+	window.location.hash = '#newmessage';
 }
 
 // Add a reply input form to the page for message with number 'id' in sub 'sub'
-var addReply = function(sub, id) {
-	if($('#replybox-' + id).length > 0)
-		return;
+function addReply(sub, id) {
+	if ($('#replybox-' + id).length > 0) return;
 	$('#li-' + id).append(
 		'<div class="reply" id="replybox-' + id + '">' +
 		'<strong>Reply</strong>' +
@@ -134,37 +133,48 @@ var addReply = function(sub, id) {
 	);
 	$.getJSON(
 		'./api/forum.ssjs?call=get-signature',
-		function(data) {
-			$('#replytext-' + id).val($('#replytext-' + id).val() + "\r\n" + data.signature);
+		function (data) {
+			$('#replytext-' + id).val(
+				$('#replytext-' + id).val() + '\r\n' + data.signature
+			);
 		}
 	);
 }
 
 // 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
-var getSubUnreadCount = function(sub) {
+function getSubUnreadCount(sub) {
 	$.getJSON(
-		"./api/forum.ssjs?call=get-sub-unread-count&sub=" + sub,
+		'./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub,
 		function(data) {
-			for(sub in data) {
-				if(data[sub].scanned > 0)
+			for (sub in data) {
+				if (data[sub].scanned > 0) {
 					$('#badge-' + sub).text(data[sub].total);
-				else if(data[sub].total > 0)
+				} else if (data[sub].total > 0) {
 					$('#badge-' + sub).text(data[sub].total);
-				else
-					$('#badge-' + sub).text("");
+				} else {
+					$('#badge-' + sub).text('');
+				}
 			}
 		}
 	);
 }
 
 // 'group' can be a single group index, or a string of 0&group=1&group=2...
-var getGroupUnreadCount = function(group) {
+function getGroupUnreadCount(group) {
 	$.getJSON(
-		"./api/forum.ssjs?call=get-group-unread-count&group=" + group,
-		function(data) {
-			for(group in data) {
-				$('#badge-scanned-' + group).text((data[group].scanned == 0 ? "" : data[group].scanned));
-				$('#badge-ignored-' + group).text((data[group].total == 0 || data[group].total == data[group].scanned ? "" : (data[group].total - data[group].scanned)));
+		'./api/forum.ssjs?call=get-group-unread-count&group=' + group,
+		function (data) {
+			for (group in data) {
+				$('#badge-scanned-' + group).text(
+					(data[group].scanned == 0 ? "" : data[group].scanned)
+				);
+				$('#badge-ignored-' + group).text(
+					(	data[group].total == 0 ||
+						data[group].total == data[group].scanned
+						? ''
+						: (data[group].total - data[group].scanned)
+					)
+				);
 			}
 		}
 	);
@@ -172,20 +182,26 @@ var getGroupUnreadCount = function(group) {
 
 /*  Fetch a private mail message's body (with links to attachments) where 'id'
 	is the message number.	Output it to an element with id 'message-<id>'. */
-var getMailBody = function(id) {
-	if(!$('#message-' + id).attr('hidden')) {
+function getMailBody(id) {
+	if (!$('#message-' + id).attr('hidden')) {
 		$('#message-' + id).attr('hidden', true);
-	} else if($('#message-' + id).html() != "") {
+	} else if ($('#message-' + id).html() != '') {
 		$('#message-' + id).attr('hidden', false);
 	} else {
 		$.getJSON(
-			"./api/forum.ssjs?call=get-mail-body&number=" + id,
-			function(data) {
+			'./api/forum.ssjs?call=get-mail-body&number=' + id,
+			function (data) {
 				var str = data.body;
-				if(data.inlines.length > 0)
-					str += "<br>Inline attachments: " + data.inlines.join("<br>") + "<br>";
-				if(data.attachments.length > 0)
-					str += "<br>Attachments: " + data.attachments.join("<br>") + "<br>";
+				if (data.inlines.length > 0) {
+					str +=
+						'<br>Inline attachments: ' +
+						data.inlines.join('<br>') + '<br>';
+				}
+				if (data.attachments.length > 0) {
+					str +=
+						'<br>Attachments: ' +
+						data.attachments.join('<br>') + '<br>';
+				}
 				str +=
 					'<button class="btn btn-default icon" ' +
 					'aria-label="Reply to this message" ' +
@@ -205,19 +221,18 @@ var getMailBody = function(id) {
 	}
 }
 
-var setScanCfg = function(sub, cfg) {
+function setScanCfg(sub, cfg) {
 	var opts = [
-		"scan-cfg-off",
-		"scan-cfg-new",
-		"scan-cfg-youonly"
+		'scan-cfg-off',
+		'scan-cfg-new',
+		'scan-cfg-youonly'
 	];
 	$.getJSON(
-		"./api/forum.ssjs?call=set-scan-cfg&sub=" + sub + "&cfg=" + cfg,
-		function(data) {
-			if(!data.success)
-				return;
+		'./api/forum.ssjs?call=set-scan-cfg&sub=' + sub + '&cfg=' + cfg,
+		function (data) {
+			if (!data.success) return;
 			opts.forEach(
-				function(opt, index) {
+				function (opt, index) {
 					$('#' + opt).toggleClass('btn-primary', (cfg == index));
 					$('#' + opt).toggleClass('btn-default', (cfg != index));
 				}
-- 
GitLab


From e5cd0cd23e3337c77663b9882c921ed01691e5a6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 01:17:22 -0500
Subject: [PATCH 058/752] Cleanup

---
 web/root/pages/000-home.xjs     |  23 +++--
 web/root/pages/000-mail.ssjs    |  58 +++++++----
 web/root/pages/000-register.xjs | 171 ++++++++++++++++++++------------
 web/root/pages/002-files.ssjs   |  22 ++--
 web/root/pages/003-games.xjs    |  28 +++---
 5 files changed, 188 insertions(+), 114 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 8480175022..be9c7a4205 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,24 +1,29 @@
 <!--Home-->
 <?xjs 
-	if(typeof argv[0] != "boolean" || !argv[0])
-		exit();
-	load(settings.web_lib + "ftelnet.js");
+	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+	load(settings.web_lib + 'ftelnet.js');
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 <div class="row">
 	<div class="center-block" style="width:200px;margin-bottom:1em;">
-		<button id="ftelnet-connect" class="btn btn-primary">Connect via Telnet</button>
+		<button id="ftelnet-connect" class="btn btn-primary">
+			Connect via Telnet
+		</button>
 	</div>
 </div>
 <script type="text/javascript">
-	fTelnet.Hostname = "<?xjs write(system.inet_addr); ?>";
+	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
 	fTelnet.Port = <?xjs write((webSocket === null) ? 1123 : webSocket.Port); ?>;
-	fTelnet.ConnectionType = "telnet";
-	fTelnet.SplashScreen = "<?xjs write(getSplash()); ?>";
+	fTelnet.ConnectionType = 'telnet';
+	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-	fTelnet.VirtualKeyboardVisible = (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
+	fTelnet.VirtualKeyboardVisible = (
+		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
+			navigator.userAgent
+		)
+	);
 	fTelnet.Init();
-	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+	$('#ftelnet-connect').click(function () { fTelnet.Connect(); });
 </script>
diff --git a/web/root/pages/000-mail.ssjs b/web/root/pages/000-mail.ssjs
index 7c62e4a1b0..a46a7a0219 100644
--- a/web/root/pages/000-mail.ssjs
+++ b/web/root/pages/000-mail.ssjs
@@ -1,18 +1,16 @@
 //HIDDEN:Mail
 
-if(typeof argv[0] != "boolean" || !argv[0])
-	exit();
+if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 
-if(user.number == 0 || user.alias == settings.guest)
-	exit();
+if (user.number === 0 || user.alias === settings.guest) exit();
 
-load("sbbsdefs.js");
-load(system.exec_dir + "../web/lib/init.js");
-load(settings.web_lib + "forum.js");
+load('sbbsdefs.js');
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'forum.js');
 
 writeln('<script type="text/javascript" src="./js/forum.js"></script>');
 
-if(typeof http_request.query.notice != "undefined") {
+if (typeof http_request.query.notice !== 'undefined') {
 	writeln(
 		'<div id="noticebox" class="alert alert-warning">' + 
 		http_request.query.notice[0] + '</div>' +
@@ -22,10 +20,8 @@ if(typeof http_request.query.notice != "undefined") {
 	);
 }
 
-if(	user.alias != settings.guest
-	&&
-	!(user.security.restrictions&UFLAG_E)
-	&&
+if (user.alias != settings.guest &&
+	!(user.security.restrictions&UFLAG_E) &&
 	!(user.security.restrictions&UFLAG_M)
 ) {
 	writeln(
@@ -48,21 +44,29 @@ writeln(
 		'<a href="./?page=%s&amp;sent=1">Sent</a>' +
 		'</li>' +
 		'</ul><br>',
-		(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? "active" : ""),
+		(	typeof http_request.query.sent === 'undefined' ||
+			http_request.query.sent[0] == '0'
+			? 'active'
+			: ''
+		),
 		http_request.query.page[0],
-		(typeof http_request.query.sent != "undefined" && http_request.query.sent[0] == "1" ? "active" : ""),
+		(	typeof http_request.query.sent !== 'undefined' &&
+			http_request.query.sent[0] == '1'
+			? 'active'
+			: ''
+		),
 		http_request.query.page[0]
 	)
 );
 
 writeln('<ul id="forum-list-container" class="list-group">');
 
-var writeMessage = function(header) {
+function writeMessage(header) {
 	writeln(
 		format(
-			'<li id="li-' + header.number + '" class="list-group-item mail striped %s">',
-			(header.attr&MSG_READ ? "read" : "unread"),
-			header.number
+			'<li id="li-%s" class="list-group-item mail striped %s">',
+			header.number,
+			(header.attr&MSG_READ ? 'read' : 'unread')
 		)
 	);
 	writeln(
@@ -72,8 +76,16 @@ var writeMessage = function(header) {
 			'<p>Subject: <strong>%s</strong></p></div>' +
 			'<div class="message" id="message-%s" hidden></div>',
 			header.number,
-			(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? "From" : "To"),
-			(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? header.from : header.to),
+			(	typeof http_request.query.sent === 'undefined' ||
+				http_request.query.sent[0] == '0'
+				? 'From'
+				: 'To'
+			),
+			(	typeof http_request.query.sent === 'undefined' ||
+				http_request.query.sent[0] == '0'
+				? header.from
+				: header.to
+			),
 			(new Date(header.when_written_time * 1000)).toLocaleString(),
 			header.subject,
 			header.number
@@ -83,7 +95,11 @@ var writeMessage = function(header) {
 }
 
 getMailHeaders(
-	(typeof http_request.query.sent == "undefined" || http_request.query.sent[0] == "0" ? false : true)
+	(	typeof http_request.query.sent === 'undefined' ||
+		http_request.query.sent[0] == '0'
+		? false
+		: true
+	)
 ).forEach(writeMessage);
 
 writeln('</ul>');
\ No newline at end of file
diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
index 917482cde7..7613ea110e 100644
--- a/web/root/pages/000-register.xjs
+++ b/web/root/pages/000-register.xjs
@@ -8,27 +8,34 @@
 -->
 
 <?xjs
-	if(user.number > 0 && user.alias != settings.guest)
-		exit();
+	if (user.number > 0 && user.alias !== settings.guest) exit();
 
-	load("sbbsdefs.js");
+	load('sbbsdefs.js');
 
-	var required = function(mask) {
-		return ((system.new_user_questions&mask) ? " required" : "");
+	function required(mask) {
+		return ((system.new_user_questions&mask) ? ' required' : '');
 	}
 
-	var notRequired = function(mask) {
-		return ((system.new_user_questions&mask) ? "" : " required");
+	function notRequired(mask) {
+		return ((system.new_user_questions&mask) ? '' : ' required');
 	}
 
-	var iconRequired = function(mask) {
-		if(required(mask).length > 0)
-			write('<span title="Required" class="glyphicon glyphicon-asterisk"></span>');
+	function iconRequired(mask) {
+		if (required(mask).length > 0) {
+			write(
+				'<span title="Required" class="glyphicon glyphicon-asterisk">' +
+				'</span>'
+			);
+		}
 	}
 
-	var iconNotRequired = function(mask) {
-		if(notRequired(mask).length > 0)
-			write('<span title="Required" class="glyphicon glyphicon-asterisk"></span>');
+	function iconNotRequired(mask) {
+		if (notRequired(mask).length > 0) {
+			write(
+				'<span title="Required" class="glyphicon glyphicon-asterisk">' +
+				'</span>'
+			);
+		}
 	}
 
 ?>
@@ -41,36 +48,53 @@
 
 <div id="form-register-container">
 
-	<div style="margin-bottom:1em;">Fields marked with <span title="Required" class="glyphicon glyphicon-asterisk"></span> are required.  All others can be left blank if you wish.</div>
+	<div style="margin-bottom:1em;">
+		Fields marked with 
+		<span title="Required" class="glyphicon glyphicon-asterisk"></span> 
+		are required.  All others can be left blank if you wish.
+	</div>
 
 	<form id="form-register" data-toggle="validator">
 
 		<div class="form-group">
 			<label for="alias">Username</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
 			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_ALIAS); ?>" class="form-control" id="alias" name="alias" placeholder="Username" required>
-			<span class="help-block">Maximum of <?xjs write(LEN_ALIAS); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_ALIAS); ?> characters
+			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="password1" class="control-label">Password</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
+			<label for="password1" class="control-label">Password</label> 
+			<span title="Required" class="glyphicon glyphicon-asterisk"></span> 
 			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="Password" required>
-			<span class="help-block">Minimum of <?xjs write(settings.minimum_password_length); ?>, maximum of <?xjs write(LEN_PASS); ?> characters</span>
+			<span class="help-block">
+				Minimum of <?xjs write(settings.minimum_password_length); ?>, 
+				maximum of <?xjs write(LEN_PASS); ?> characters
+			</span>
 		</div>
 			<div class="form-group">
-			<label for="password2" class="control-label">Confirm Password</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
+			<label for="password2" class="control-label">Confirm Password</label> 
+			<span title="Required" class="glyphicon glyphicon-asterisk"></span> 
 			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="Confirm Password" data-match="#password1" required>
 		</div>
 
 		<div class="form-group">
-			<label for="netmail">Email address</label> <?xjs iconNotRequired(UQ_NONETMAIL); ?>
+			<label for="netmail">Email address</label> 
+			<?xjs iconNotRequired(UQ_NONETMAIL); ?>
 			<input type="email" data-minlength="6" maxlength="<?xjs write(LEN_NETMAIL); ?>"class="form-control" id="netmail" name="netmail" placeholder="pat@m.f"<?xjs write(notRequired(UQ_NONETMAIL)); ?>>
-			<span class="help-block">Maximum of <?xjs write(LEN_NETMAIL); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_NETMAIL); ?> characters
+			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="realname">Real Name</label> <?xjs iconRequired(UQ_REALNAME); ?>
+			<label for="realname">Real Name</label> 
+			<?xjs iconRequired(UQ_REALNAME); ?>
 			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_NAME); ?>" class="form-control" id="realname" name="realname" placeholder="Pat Androgyne"<?xjs write(required(UQ_REALNAME)); ?>>
-			<span class="help-block">Maximum of <?xjs write(LEN_NAME); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_NAME); ?> characters
+			</span>
 		</div>
 
 <!--	Not sure which User property this would be
@@ -80,27 +104,38 @@
 		</div> -->
 
 		<div class="form-group">
-			<label for="address">Street address</label> <?xjs iconRequired(UQ_ADDRESS); ?>
+			<label for="address">Street address</label> 
+			<?xjs iconRequired(UQ_ADDRESS); ?>
 			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="address" name="address" placeholder="123 Any Street"<?xjs write(required(UQ_ADDRESS)); ?>>
-			<span class="help-block">Maximum of <?xjs write(LEN_ADDRESS); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_ADDRESS); ?> characters
+			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="location">Location</label> <?xjs iconRequired(UQ_LOCATION); ?>
+			<label for="location">Location</label> 
+			<?xjs iconRequired(UQ_LOCATION); ?>
 			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_LOCATION); ?>" class="form-control" id="location" name="location" placeholder="City, State"<?xjs write(required(UQ_LOCATION)); ?>>
-			<span class="help-block">Maximum of <?xjs write(LEN_LOCATION); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_LOCATION); ?> characters
+			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="phone">Telephone number</label> <?xjs iconRequired(UQ_PHONE); ?>
+			<label for="phone">Telephone number</label> 
+			<?xjs iconRequired(UQ_PHONE); ?>
 			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_PHONE); ?>" class="form-control" id="phone" name="phone" placeholder="800-555-5555"<?xjs write(required(UQ_PHONE)); ?>>
-			<span class="help-block">Maximum of <?xjs write(LEN_PHONE); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_PHONE); ?> characters
+			</span>
 		</div>
 
 		<div class="form-group">
 			<label for="birth">Birthdate</label> <?xjs iconRequired(UQ_BIRTH); ?>
 			<input type="text" data-minlength="<?xjs write(LEN_BIRTH); ?>" maxlength="<?xjs write(LEN_BIRTH); ?>" class="form-control" id="birth" name="birth" placeholder="<?xjs write(system.settings&SYS_EURODATE ? 'DD/MM/YY' : 'MM/DD/YY'); ?>" <?xjs write(required(UQ_BIRTH)); ?>>
-			<span class="help-block">Maximum of <?xjs write(LEN_BIRTH); ?> characters</span>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_BIRTH); ?> characters
+			</span>
 		</div>
 
 		<div class="form-inline">
@@ -129,10 +164,15 @@
 		<br>
 
 		<?xjs
-			if(system.newuser_password != "") {
+			if(system.newuser_password !== '') {
 				writeln('<div class="form-group">');
-				writeln('<label for="newuser-password">Registration password</label>');
-				writeln('<input type="password" id="newuser-password" name="newuser-password" data-minlength="1" maxlength="8">');
+				writeln(
+					'<label for="newuser-password">Registration password</label>'
+				);
+				writeln(
+					'<input type="password" id="newuser-password" ' +
+					'name="newuser-password" data-minlength="1" maxlength="8">'
+				);
 				writeln('</div>');
 			}
 		?>
@@ -153,40 +193,41 @@
 
 <script type="text/javascript">
 
-var register = function() {
-	$.post(
-	 	"./api/register.ssjs",
-		$('#form-register').serialize()
-	).done(
-	 	function(data) {
-	 		if(data.errors.length > 0) {
-	 			$('#errorbox').html("");
-	 			data.errors.forEach(
-	 				function(err) {
-	 					$('#errorbox').append("<p>" + err + "</p>");
-	 				}
-	 			);
-	 			$('#errorbox').attr("hidden", false);
-	 		} else {
-	 			$('#errorbox').attr("hidden", true);
-	 			$('#form-register-container').html("Your account has been created.");
-	 		}
-	 	}
-	);
-}
-
-$('#form-register').validator().on(
-	'submit',
-	function(evt) {
-		if(evt.isDefaultPrevented())
-			return;
-		evt.preventDefault();
-		register();
+	function register() {
+		$.post(
+		 	'./api/register.ssjs',
+			$('#form-register').serialize()
+		).done(
+		 	function (data) {
+		 		if (data.errors.length > 0) {
+		 			$('#errorbox').html('');
+		 			data.errors.forEach(
+		 				function (err) {
+		 					$('#errorbox').append('<p>' + err + '</p>');
+		 				}
+		 			);
+		 			$('#errorbox').attr('hidden', false);
+		 		} else {
+		 			$('#errorbox').attr('hidden', true);
+		 			$('#form-register-container').html(
+		 				'Your account has been created.'
+		 			);
+		 		}
+		 	}
+		);
 	}
-);
 
-// Traps
-$('#send-me-free-stuff').attr('hidden', true);
-$('#subscribe-to-newsletter').attr('hidden', true);
+	$('#form-register').validator().on(
+		'submit',
+		function (evt) {
+			if (evt.isDefaultPrevented()) return;
+			evt.preventDefault();
+			register();
+		}
+	);
+
+	// Traps
+	$('#send-me-free-stuff').attr('hidden', true);
+	$('#subscribe-to-newsletter').attr('hidden', true);
 
 </script>
\ No newline at end of file
diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index cbbd45af66..b3f54b3b2c 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -1,7 +1,6 @@
 //Files
 
-if(typeof argv[0] !== 'boolean' || !argv[0])
-	exit();
+if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'files.js');
@@ -19,10 +18,13 @@ if (typeof http_request.query.dir !== 'undefined' &&
 		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
 		'</li>' +
 		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + file_area.dir[http_request.query.dir[0]].lib_index + '">' + file_area.dir[http_request.query.dir[0]].lib_name + '</a>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' +
+		file_area.dir[http_request.query.dir[0]].lib_index + '">' +
+		file_area.dir[http_request.query.dir[0]].lib_name + '</a>' +
 		'</li>' +
 		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;dir=' + http_request.query.dir[0] + '">' + http_request.query.dir[0] + '</a>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;dir=' + 
+		http_request.query.dir[0] + '">' + http_request.query.dir[0] + '</a>' +
 		'</li>' +
 		'</ol>'
 	);
@@ -30,7 +32,8 @@ if (typeof http_request.query.dir !== 'undefined' &&
 	function writeFileDetails(file) {
 		writeln(
 			format(
-				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
+				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" ' +
+				'target="_blank" class="list-group-item striped">' +
 				'<strong>%s</strong> (%s)' +
 				'<p><em>Uploaded %s</em></p>' +
 				'<p>%s</p>' +
@@ -42,7 +45,10 @@ if (typeof http_request.query.dir !== 'undefined' &&
 				file.size,
 				system.timestr(file.uploadDate),
 				file.description,
-				(file.extendedDescription == "" ? "" : ("<p>" + file.extendedDescription + "</p>"))
+				(	file.extendedDescription == ''
+					? ''
+					: ('<p>' + file.extendedDescription + '</p>')
+				)
 			)
 		);
 	}
@@ -64,7 +70,9 @@ if (typeof http_request.query.dir !== 'undefined' &&
 		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
 		'</li>' +
 		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + http_request.query.library[0] + '">' + file_area.lib_list[http_request.query.library[0]].name + '</a>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' +
+		http_request.query.library[0] + '">' +
+		file_area.lib_list[http_request.query.library[0]].name + '</a>' +
 		'</li>' +
 		'</ol>'
 	);
diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index 93f6e7e3c0..9452e5ca9b 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -1,8 +1,7 @@
 <!--Games-->
 <?xjs 
-	if(typeof argv[0] != "boolean" || !argv[0])
-		exit();
-	load(settings.web_lib + "ftelnet.js");
+	if (typeof argv[0] != "boolean" || !argv[0]) exit();
+	load(settings.web_lib + 'ftelnet.js');
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
@@ -11,17 +10,21 @@
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 
 <script type="text/javascript">
-	fTelnet.Hostname = "<?xjs write(system.inet_addr); ?>";
+	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
 	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
-	fTelnet.ConnectionType = "telnet";
-	fTelnet.SplashScreen = "<?xjs write(getSplash()); ?>";
+	fTelnet.ConnectionType = 'telnet';
+	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-	fTelnet.VirtualKeyboardVisible = (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
+	fTelnet.VirtualKeyboardVisible = (
+		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
+			navigator.userAgent
+		)
+	);
 	$(document).ready(fTelnet.Init());
-	var launchXtrn = function(code) {
+	function launchXtrn(code) {
 		$.getJSON(
 			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function(data) {
+			function (data) {
 				fTelnet.Connect();
 			}
 		);
@@ -33,15 +36,16 @@
 <?xjs
 
 	settings.xtrn_sections.forEach(
-		function(section) {
+		function (section) {
 			writeln('<div class="list-group-item">');
 			writeln('<h4>' + xtrn_area.sec[section].name + '</h4>');
 			writeln('<ul class="nav nav-pills">');
 			xtrn_area.sec_list[xtrn_area.sec[section].index].prog_list.forEach(
-				function(program) {
+				function (program) {
 					writeln(
 						'<li role="presentation">' +
-						'<a href="#fTelnet" onclick="launchXtrn(\'' + program.code + '\')">' +
+						'<a href="#fTelnet" onclick="launchXtrn(\'' +
+							program.code + '\')">' +
 						program.name +
 						'</a>' +
 						'</li>'
-- 
GitLab


From 5653451d3f71bb6df452133e0f70cd147247d3e4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 01:53:23 -0500
Subject: [PATCH 059/752] Cleanup

---
 web/root/sidebar/001-nodelist.xjs | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/web/root/sidebar/001-nodelist.xjs b/web/root/sidebar/001-nodelist.xjs
index 04722b392f..2126946bab 100644
--- a/web/root/sidebar/001-nodelist.xjs
+++ b/web/root/sidebar/001-nodelist.xjs
@@ -2,18 +2,18 @@
 <script type="text/javascript">
 var nodeList = function() {
 	$.getJSON(
-		"./api/system.ssjs?call=node-list",
-		function(data) {
+		'./api/system.ssjs?call=node-list',
+		function (data) {
 			$('#sbbs-nodelist').html(
 				'<h4>Who\'s online</h4>' +
 				'<table id="sbbs-nodelist-table" ' +
-				'class="table table-condensed table-responsive table-striped">' +
+				'class="table table-condensed table-responsive table-striped">'+
 				'<thead><tr><th>Node</th><th>Status</th></tr></thead>' +
 				'<tbody></tbody>' +
 				'</table>'
 			);
 			data.forEach(
-				function(n, index) {
+				function (n, index) {
 					var nll = <?xjs write(system.node_list.length); ?>;
 					$('#sbbs-nodelist-table').append(
 						'<tr>' +
@@ -21,15 +21,25 @@ var nodeList = function() {
 						(index >= nll ? "W" : (index + 1)) +
 						'</th>' +
 						'<td id="nodelist-' + index + '">' +
-						(n.user == "" ? n.status : ('<strong>' + n.user + '</strong> ' + n.action)) +
+						(	n.user == ''
+							? n.status
+							: ('<strong>' + n.user + '</strong> ' + n.action)
+						) +
 						'</td>' +
 						'</tr>'
 					);
-					if(n.user != "" && <?xjs write(user.alias!=settings.guest); ?>) {
-						$('#nodelist-' + index).attr('title', "Send a telegram");
-						$('#nodelist-' + index).css('cursor', "pointer");
+					if (n.user != '' &&
+						<?xjs write(user.alias!=settings.guest); ?>
+					) {
+						$('#nodelist-' + index).attr(
+							'title',
+							'Send a telegram'
+						);
+						$('#nodelist-' + index).css('cursor', 'pointer');
 						$('#nodelist-' + index).click(
-							function() { sendTelegram(n.user); }
+							function () {
+								sendTelegram(n.user);
+							}
 						);
 					}
 				}
-- 
GitLab


From 5af85a841500f29f79ea61ca1fe4194637b80a63 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 02:50:06 -0500
Subject: [PATCH 060/752] Cleanup

---
 web/lib/auth.js    |  6 +++---
 web/lib/files.js   | 22 ++++++++++----------
 web/lib/ftelnet.js | 13 ++++++------
 web/lib/init.js    | 50 +++++++++++++++++++++++++---------------------
 web/lib/sidebar.js | 46 ++++++++++++++++++------------------------
 5 files changed, 65 insertions(+), 72 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index bf59ac1fae..23f5fa3d88 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -1,5 +1,5 @@
-load("sbbsdefs.js");
-load(system.exec_dir + "../web/lib/init.js");
+load('sbbsdefs.js');
+load(system.exec_dir + '../web/lib/init.js');
 
 function randomString(length) {
 	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split("");
@@ -132,7 +132,7 @@ function authenticate(alias, password) {
 	if (usr.security.password.toUpperCase() !== password.toUpperCase()) {
 		return false;
 	}
-	login(usr.alias, usr.security.password.toUpperCase());
+	login(usr.alias, usr.security.password);
 	return usr;
 }
 
diff --git a/web/lib/files.js b/web/lib/files.js
index 34cd386837..e078fbdd47 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -1,35 +1,33 @@
-load("filedir.js");
-load("file_size.js");
+load('filedir.js');
+load('file_size.js');
 
-var listLibraries = function() {
+function listLibraries() {
 	var libraries = [];
 	file_area.lib_list.forEach(
-		function(library) {
-			if(library.dir_list.length > 0)
-				libraries.push(library);
+		function (library) {
+			if (library.dir_list.length > 0) libraries.push(library);
 		}
 	);
 	return libraries;
 }
 
-var listDirectories = function(library) {
+function listDirectories(library) {
 	var dirs = [];
 	file_area.lib_list[library].dir_list.forEach(
-		function(dir) {
+		function (dir) {
 			var fd = new FileDir(dir);
-			if(fd.files.length < 1)
-				return;
+			if (fd.files.length < 1) return;
 			dirs.push({'dir' : dir, 'fileCount' : fd.files.length });
 		}
 	);
 	return dirs;
 }
 
-var listFiles = function(dir) {
+function listFiles(dir) {
 	var files = [];
 	var fd = new FileDir(file_area.dir[dir]);
 	fd.files.forEach(
-		function(dirFile) {
+		function (dirFile) {
 			dirFile.size = file_size_str(file_size(dirFile.fullPath));
 			files.push(dirFile);
 		}
diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index 22d926318b..9957af8474 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -1,14 +1,13 @@
 var f = new File(file_cfgname(system.ctrl_dir, "services.ini"));
-if(!f.open("r"))
-	exit();
-var webSocket = f.iniGetObject("WebSocket");
-var webSocketRLogin = f.iniGetObject("WebSocketRLogin");
+if (!f.open('r')) exit();
+var webSocket = f.iniGetObject('WebSocket');
+var webSocketRLogin = f.iniGetObject('WebSocketRLogin');
 f.close();
 
-var getSplash = function() {
+function getSplash() {
 	var f = new File(settings.ftelnet_splash);
-	f.open("rb");
+	f.open('rb');
 	var splash = base64_encode(f.read());
 	f.close();
 	return splash;
-}
+}
\ No newline at end of file
diff --git a/web/lib/init.js b/web/lib/init.js
index 53d1c27935..3974e060e3 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,51 +1,55 @@
-load("modopts.js");
+load('modopts.js');
 
-var settings = get_mod_options("web");
+var settings = get_mod_options('web');
 
 // Paths
 settings.web_directory = fullpath(
 	backslash(
-		typeof settings.web_directory == "undefined" ? "../web" : settings.web_directory
+		typeof settings.web_directory === 'undefined'
+		? '../web'
+		: settings.web_directory
 	)
 );
-settings.web_root = backslash(settings.web_directory + "root/");
-settings.web_lib = backslash(settings.web_directory + "lib/");
+settings.web_root = backslash(settings.web_directory + 'root/');
+settings.web_lib = backslash(settings.web_directory + 'lib/');
 
 // Guest
-if(typeof settings.guest == "undefined")
-	settings.guest = "Guest";
-if(system.matchuser(settings.guest) == 0)
-	exit();
+if (typeof settings.guest === 'undefined') settings.guest = 'Guest';
+if (system.matchuser(settings.guest) == 0) exit();
 
 // Timeout
-if(typeof settings.timeout != "number")
-	settings.timeout = 43200;
+if (typeof settings.timeout !== 'number') settings.timeout = 43200;
 
 // Registration
-if(typeof settings.user_registration != "boolean") {
+if (typeof settings.user_registration !== 'boolean') {
 	settings.user_registration = false;
 } else {
 
-	if(typeof settings.minimum_password_length != "number")
+	if (typeof settings.minimum_password_length !== 'number') {
 		settings.minimum_password_length = 4;
+	}
 
-	if(typeof settings.email_validation != "boolean")
+	if (typeof settings.email_validation !== 'boolean') {
 		settings.email_validation = true;
+	}
 
-	if(typeof settings.email_validation_level != "number")
+	if (typeof settings.email_validation_level !== 'number') {
 		settings.email_validation_level = 50;
+	}
 
 }
 
-if(typeof settings.xtrn_sections == "string") {
-	settings.xtrn_sections = settings.xtrn_sections.split(",").filter(
-		function(section) {
-			if(typeof xtrn_area.sec[section] == "undefined")
-				return false;
-			if(!xtrn_area.sec[section].can_access)
-				return false;
-			if(xtrn_area.sec_list[xtrn_area.sec[section].index].prog_list.length < 1)
+if (typeof settings.xtrn_sections === 'string') {
+	settings.xtrn_sections = settings.xtrn_sections.split(',').filter(
+		function (section) {
+			if (typeof xtrn_area.sec[section] === 'undefined') return false;
+			if (!xtrn_area.sec[section].can_access) return false;
+			if (xtrn_area.sec_list[
+					xtrn_area.sec[section].index
+				].prog_list.length < 1
+			) {
 				return false;
+			}
 			return true;	
 		}
 	);
diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index 347aca1330..d9694aa19d 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -1,7 +1,6 @@
-var getSidebarModules = function() {
-
+function getSidebarModules() {
 	var sidebarModules = [];
-	var d = directory(settings.web_root + "sidebar/*");
+	var d = directory(settings.web_root + 'sidebar/*');
 	d.forEach(
 		function(item) {
 			if(file_isdir(item))
@@ -12,40 +11,35 @@ var getSidebarModules = function() {
 		}
 	);
 	return sidebarModules;
-
 }
 
-var getSidebarModule = function(module) {
-
-	var ret = "";
-
-	if(!file_exists(module))
-		return ret;
+function getSidebarModule(module) {
 
+	var ret = '';
+	if (!file_exists(module)) return ret;
 	var ext = file_getext(module).toUpperCase();
 
-	switch(ext) {
-		case ".SSJS":
-			if(ext == ".SSJS" && module.search(/\.xjs\.ssjs$/i) >= 0)
-				break;
+	switch (ext) {
+		case '.SSJS':
+			if (ext === '.SSJS' && module.search(/\.xjs\.ssjs$/i) >= 0) break;
 			load(module, true);
 			break;
-		case ".XJS":
+		case '.XJS':
 			load(xjs_compile(module), true);
 			break;
-		case ".HTML":
+		case '.HTML':
 			var f = new File(module);
-			f.open("r");
-			if(f.is_open) {
+			f.open('r');
+			if (f.is_open) {
 				ret = f.read();
 				f.close();
 			}
 			break;
-		case ".TXT":
+		case '.TXT':
 			var f = new File(module);
 			f.open();
-			if(f.is_open) {
-				ret = "<pre>" + f.read() + "</pre>";
+			if (f.is_open) {
+				ret = '<pre>' + f.read() + '</pre>';
 				f.close();
 			}
 			break;
@@ -57,17 +51,15 @@ var getSidebarModule = function(module) {
 
 }
 
-var writeSidebarModules = function() {
+function writeSidebarModules() {
 	var modules = getSidebarModules();
 	write('<ul class="list-group">');
 	modules.forEach(
-		function(module) {
-			if(module.search(/\.xjs\.ssjs$/i) >= 0)
-				return;
+		function (module) {
+			if (module.search(/\.xjs\.ssjs$/i) >= 0) return;
 			write('<li class="list-group-item sidebar">');
 			var str = getSidebarModule(settings.web_root + "sidebar/" + module);
-			if(str != "")
-				write(str);
+			if (str !== '') write(str);
 			write('</li>');
 		}
 	);
-- 
GitLab


From 024eec8d7b0ce689ff4a8b425d58ec648b60fafa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 15:24:13 -0500
Subject: [PATCH 061/752] Initial keyboard navigation stuff for message
 reading.  Left/right arrow keys jump to previous/next message in thread.

---
 web/root/css/style.css        |   5 ++
 web/root/js/forum.js          |  46 +++++++++++
 web/root/pages/001-forum.ssjs | 150 ++++++++++++++++++++++++++--------
 3 files changed, 166 insertions(+), 35 deletions(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index bebe652486..8ff0a26927 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -37,6 +37,11 @@ blockquote {
 	border-left : 5px solid #A4A4A4;
 }
 
+li.current {
+	border-left-color : #000000;
+	border-right-color : #000000;
+}
+
 /* Unused? */
 span.message-header.unread {
 	background-color: #FCF8E3;
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 7a3eb90415..f54337f90f 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -240,3 +240,49 @@ function setScanCfg(sub, cfg) {
 		}
 	);
 }
+
+function threadNav() {
+
+	if (window.location.hash === '') {
+		$($('#forum-list-container').children('.list-group-item')[0]).addClass(
+			'current'
+		);
+	} else if ($('#li-' + window.location.hash.substr(1)).length > 0) {
+		$('#li-' + window.location.hash.substr(1)).addClass('current');
+	}
+
+	$(window).keydown(
+		function (evt) {
+			var cid = $(
+				$('#forum-list-container').children('.current')[0]
+			).attr(
+				'id'
+			).substr(3);
+			switch (evt.keyCode) {
+				case 37:
+					// Left
+					window.location.hash = $('#pm-' + cid).attr('href');
+					break;
+				case 39:
+					// Right
+					window.location.hash = $('#nm-' + cid).attr('href');
+					break;
+				default:
+					break;
+			}
+		}
+	);
+
+	$(window).on(
+		'hashchange',
+		function () {
+			$('#forum-list-container').children('.current').removeClass(
+				'current'
+			);
+			var id = window.location.hash.substr(1);
+			if ($('#li-' + id).length < 1) return;
+			$('#li-' + id).addClass('current');
+		}
+	);
+
+}
\ No newline at end of file
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index c403c6b5cd..08aded54d1 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -35,9 +35,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		if (body === null) return;
 
 		writeln(
+			'<a id="' + header.number + '"></a>' +
 			'<li class="list-group-item striped' +
-			'" id="li-' + header.number + '">' +
-			'<a id="' + header.number + '"></a>'
+			'" id="li-' + header.number + '">'
 		);
 
 		// Show subject if first message
@@ -67,6 +67,18 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			formatMessage(body) + '</div>'
 		);
 
+		var prev = (
+			index === 0
+			? header.ec_thread.messages[0].number
+			: header.ec_thread.messages[index - 1].number
+		);
+
+		var next = (
+			index === header.ec_thread.messages.length - 1
+			? header.ec_thread.messages[header.ec_thread.messages.length - 1].number
+			: header.ec_thread.messages[index + 1].number
+		);
+
 		// Standard controls
 		writeln(
 			'<a class="btn btn-default icon" title="Jump to oldest message" ' +
@@ -74,12 +86,28 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			header.ec_thread.messages[0].number + '">' +
 			'<span class="glyphicon glyphicon-fast-backward"></span></a>' +
 
-			'<a class="btn btn-default icon" title="Direct link to this message" ' +
-			'aria-label="Direct link to this message" href="#' + header.number + '">' +
+			'<a class="btn btn-default icon" title="Jump to previous message" '+
+			'aria-label="Jump to previous message" href="#' + prev + '" ' +
+			'id="pm-' + header.number + '">' +
+			'<span class="glyphicon glyphicon-step-backward"></span></a>' +
+
+			'<a class="btn btn-default icon" ' +
+			'title="Direct link to this message" ' +
+			'aria-label="Direct link to this message" ' +
+			'href="#' + header.number + '">' +
 			'<span class="glyphicon glyphicon-link"></span></a>' +
+
+			'<a class="btn btn-default icon" title="Jump to next message" ' +
+			'aria-label="Jump to next message" href="#' + next + '" ' +
+			'id="nm-' + header.number + '">' +
+			'<span class="glyphicon glyphicon-step-forward"></span></a>' +
 			
-			'<a class="btn btn-default icon" aria-label="Jump to newest message" title="Jump to newest message" href="#' + 
-			header.ec_thread.messages[header.ec_thread.messages.length - 1].number + 
+			'<a class="btn btn-default icon" ' +
+			'aria-label="Jump to newest message" ' +
+			'title="Jump to newest message" href="#' + 
+			header.ec_thread.messages[
+				header.ec_thread.messages.length - 1
+			].number + 
 			'"><span class="glyphicon glyphicon-fast-forward"></span></a>'
 		);
 
@@ -92,7 +120,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				'aria-label="Reply to this message" ' +
 				'title="Reply to this message" ' +
 				'name="reply-' + header.number + '" ' +
-				'onclick="addReply(\'' + msgBase.cfg.code + '\',' + header.number + ')">' +
+				'onclick="addReply(\'' +
+					msgBase.cfg.code + '\',' + header.number +
+				')">' +
 				'<span class="glyphicon glyphicon-comment"></span>' +
 				'</button>'
 			);
@@ -104,8 +134,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		) {
 			writeln(
 				'<button class="btn btn-default icon" ' +
-				'aria-label="Delete this message" title="Delete this message" ' +
-				'onclick="deleteMessage(\'' + msgBase.cfg.code + '\', ' + header.number + ')" ' +
+				'aria-label="Delete this message" title="Delete this message" '+
+				'onclick="deleteMessage(\'' +
+					msgBase.cfg.code + '\', ' + header.number +
+				')" ' +
 				'href="#">' +
 				'<span class="glyphicon glyphicon-trash"></span>' +
 				'</button>'
@@ -134,7 +166,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	);
 	writeln(
 		'<div id="jump-unread-container" style="margin-bottom:1em;" hidden>' +
-		'<a class="btn btn-default" id="jump-unread" title="Jump to first unread message" href="#">' +
+		'<a class="btn btn-default" id="jump-unread" ' +
+		'title="Jump to first unread message" href="#">' +
 		'<span class="glyphicon glyphicon-star"></span>' +
 		'</a></div>'
 	);
@@ -159,13 +192,18 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			);
 		}
 		// Update scan pointer
-		if (messages[messages.length - 1].number > msg_area.sub[http_request.query.sub[0]].scan_ptr) {
-			msg_area.sub[http_request.query.sub[0]].scan_ptr = messages[messages.length - 1].number;
+		if (messages[messages.length - 1].number >
+			msg_area.sub[http_request.query.sub[0]].scan_ptr
+		) {
+			msg_area.sub[http_request.query.sub[0]].scan_ptr =
+			messages[messages.length - 1].number;
 		}
 	} catch (err) {
 		log(err);
 	}
 
+	writeln('<script type="text/javascript">threadNav();</script>');
+
 } else if (
 	typeof http_request.query.sub !== 'undefined' &&
 	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined'
@@ -180,19 +218,30 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		}
 		writeln(
 			format(
-				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" class="list-group-item striped">' +
+				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" ' +
+				'class="list-group-item striped">' +
 				'<strong>%s</strong>' +
 				'<p>By <strong>%s</strong> on %s</p>' +
-				'<span title="Unread messages" class="badge%s" id="badge-%s">%s</span>' +
+				'<span title="Unread messages" class="badge%s" ' +
+				'id="badge-%s">%s</span>' +
 				'<p>Latest reply by <strong>%s</strong> on %s</p>' +
 				'</a>',
 				http_request.query.page[0],
 				http_request.query.sub[0],
-				(thread.messages[0].thread_id == 0 ? thread.messages[0].number : thread.messages[0].thread_id),
+				(	thread.messages[0].thread_id == 0
+					? thread.messages[0].number
+					: thread.messages[0].thread_id
+				),
 				thread.messages[0].subject,
 				thread.messages[0].from,
-				(new Date(thread.messages[0].when_written_time * 1000)).toLocaleString(),
-				((msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) ? " scanned" : ""),
+				(new Date(
+						thread.messages[0].when_written_time * 1000
+					)
+				).toLocaleString(),
+				(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW
+					? ' scanned'
+					: ''
+				),
 				thread.messages[0].number,
 				(unread == 0 ? "" : unread),
 				thread.messages[thread.messages.length - 1].from,
@@ -237,8 +286,13 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	if (user.alias !== settings.guest) {
 		writeln(
 			'<button id="scan-cfg-new" class="btn ' +
-			(!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) && (msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) ? "btn-primary" : "btn-default") +
-			' icon" aria-label="Scan for new messages" title="Scan for new messages" ' +
+			(	!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) &&
+				(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW)
+				? 'btn-primary'
+				: 'btn-default'
+			) +
+			' icon" aria-label="Scan for new messages" ' +
+			'title="Scan for new messages" ' +
 			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',1)"' +
 			'>' +
 			'<span class="glyphicon glyphicon-ok-sign"></span>' +
@@ -246,8 +300,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		);
 		writeln(
 			'<button id="scan-cfg-youonly" class="btn ' +
-			((msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) ? "btn-primary" : "btn-default") +
-			' icon" aria-label="Scan for new messages to you only" title="Scan for new messages to you only" ' +
+			(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY
+				? 'btn-primary'
+				: 'btn-default'
+			) +
+			' icon" aria-label="Scan for new messages to you only" ' +
+			'title="Scan for new messages to you only" ' +
 			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',2)"' +
 			'>' +
 			'<span class="glyphicon glyphicon-user"></span>' +
@@ -255,8 +313,13 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		);
 		writeln(
 			'<button id="scan-cfg-off" class="btn ' +
-			(!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) && !(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) ? "btn-primary" : "btn-default") +
-			' icon" aria-label="Do not scan this sub" title="Do not scan this sub" ' +
+			(	!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) &&
+				!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY)
+				? 'btn-primary'
+				: 'btn-default'
+			) +
+			' icon" aria-label="Do not scan this sub" ' +
+			'title="Do not scan this sub" ' +
 			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',0)"' +
 			'>' +
 			'<span class="glyphicon glyphicon-ban-circle"></span>' +
@@ -282,18 +345,24 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	function writeSub(sub) {
 		writeln(
 			format(
-				'<a href="./?page=%s&amp;sub=%s" class="list-group-item striped%s">' +
+				'<a href="./?page=%s&amp;sub=%s" ' +
+				'class="list-group-item striped%s">' +
 				'<h4><strong>%s</strong></h4>' +
-				'<span title="Unread messages" class="badge %s" id="badge-%s">' +
-				//'<img src="./images/ajax-loader-small.gif">' +
+				'<span title="Unread messages" class="badge %s" id="badge-%s">'+
 				'</span>' +
 				'<p>%s</p>' +
 				'</a>',
 				http_request.query.page[0],
 				sub.code,
-				((sub.scan_cfg&SCAN_CFG_NEW) || (sub.scan_cfg&SCAN_CFG_YONLY) ? " scanned" : ""),
+				(	sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY
+					? ' scanned'
+					: ''
+				),
 				sub.name,
-				((sub.scan_cfg&SCAN_CFG_NEW) || (sub.scan_cfg&SCAN_CFG_YONLY) ? "scanned" : "total"),
+				(	sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY
+					? 'scanned'
+					: 'total'
+				),
 				sub.code,
 				sub.description
 			)
@@ -306,7 +375,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		subs.forEach(function (sub) { codes.push(sub.code); });
 		codes = codes.join('&sub=');
 		writeln('getSubUnreadCount("' + codes + '");');
-		writeln('setInterval(function(){getSubUnreadCount("' + codes + '");}, 60000);');
+		writeln(
+			'setInterval(' +
+				'function(){getSubUnreadCount("' + codes + '");},' +
+				'60000' +
+			');'
+		);
 		writeln('</script>');
 	}
 
@@ -341,13 +415,14 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	function writeGroup(group) {
 		writeln(
 			format(
-				'<a href="./?page=%s&amp;group=%s" class="list-group-item striped">' +
+				'<a href="./?page=%s&amp;group=%s" ' +
+				'class="list-group-item striped">' +
 				'<h3><strong>%s</strong></h3>' +
-				'<span title="Unread messages (other)" class="badge ignored" id="badge-ignored-%s">' +
-				//'<img src="./images/ajax-loader-small.gif">' +
+				'<span title="Unread messages (other)" ' +
+				'class="badge ignored" id="badge-ignored-%s">' +
 				'</span>' +
-				'<span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-%s">' +
-				//'<img src="./images/ajax-loader-small.gif">' +
+				'<span title="Unread messages (scanned subs)" ' +
+				'class="badge scanned" id="badge-scanned-%s">' +
 				'</span>' +
 				'<p>%s : %s sub-boards</p>' +
 				'</a>',
@@ -368,7 +443,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		groups.forEach(function(group) { indexes.push(group.index); });
 		indexes = indexes.join('&group=');
 		writeln('getGroupUnreadCount("' + indexes + '");');
-		writeln('setInterval(function(){getGroupUnreadCount("' + indexes + '");},60000);');
+		writeln(
+			'setInterval(' +
+				'function(){getGroupUnreadCount("' + indexes + '");},' +
+				'60000' +
+			');'
+		);
 		writeln('</script>');
 	}
 	
-- 
GitLab


From 79287298db493d0fdb8ab81df97603afd0307e6d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 15:58:18 -0500
Subject: [PATCH 062/752] Comment re: li.current rule

---
 web/root/css/style.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 8ff0a26927..4326867de5 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -37,6 +37,7 @@ blockquote {
 	border-left : 5px solid #A4A4A4;
 }
 
+/* The 'current' message being viewed in a thread. */
 li.current {
 	border-left-color : #000000;
 	border-right-color : #000000;
-- 
GitLab


From 5658f2d956a0f4d61e65b3eeede7c76a25842a77 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 16:12:36 -0500
Subject: [PATCH 063/752] Fix incorrect anchor href for first-unread-message
 link.

---
 web/root/pages/001-forum.ssjs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 08aded54d1..acec717d18 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -27,7 +27,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	// Thread view
 	var msgBase = new MsgBase(http_request.query.sub[0]);
 
-	var firstUnreadLi = '';
+	var firstUnread = '';
 
 	function writeMessage(header, index) {
 
@@ -52,7 +52,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			writeln(
 				'<span title="Unread" class="glyphicon glyphicon-star"></span>'
 			);
-			if (firstUnreadLi === '') firstUnreadLi = 'li-' + header.number;
+			if (firstUnread === '') firstUnread += header.number;
 		}			
 		writeln(
 			'From <strong>' + header.from + "</strong>" +
@@ -183,10 +183,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		messages.forEach(writeMessage);
 		writeln('</ul>');
 		msgBase.close();
-		if (messages.length > 1 && firstUnreadLi !== '') {
+		if (messages.length > 1 && firstUnread !== '') {
 			writeln(
 				'<script type="text/javascript">' +
-				'$("#jump-unread").attr("href", "#' + firstUnreadLi + '");' +
+				'$("#jump-unread").attr("href", "#' + firstUnread + '");' +
 				'$("#jump-unread-container").attr("hidden", false);' +
 				'</script>'
 			);
-- 
GitLab


From a9655c20551914bb175a2ecb957a4ac04d4a1a8a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 17:15:26 -0500
Subject: [PATCH 064/752] Cleanup

---
 web/lib/forum.js | 134 ++++++++++++++++++++++-------------------------
 1 file changed, 64 insertions(+), 70 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 9256ef5b9d..ec8697293b 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -3,17 +3,16 @@ load('msgutils.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
 
-function listGroups () {
+function listGroups() {
     var response = [];
     msg_area.grp_list.forEach(
         function (grp) {
-            if (grp.sub_list.length < 1)
-                return;
+            if (grp.sub_list.length < 1) return;
             response.push(
-                {   'index' : grp.index,
-                    'name' : grp.name,
-                    'description' : grp.description,
-                    'sub_count' : grp.sub_list.length
+                {   index : grp.index,
+                    name : grp.name,
+                    description : grp.description,
+                    sub_count : grp.sub_list.length
                 }
             );
         }
@@ -22,32 +21,32 @@ function listGroups () {
 }
 
 // Returns an array of objects of "useful" information about subs
-function listSubs (group) {
+function listSubs(group) {
     var response = [];
     msg_area.grp_list[group].sub_list.forEach(
         function (sub) {
             response.push(
-                {   'index' : sub.index,
-                    'code' : sub.code,
-                    'grp_index' : sub.grp_index,
-                    'grp_name' : sub.grp_name,
-                    'name' : sub.name,
-                    'description' : sub.description,
-                    'qwk_name' : sub.qwk_name,
-                    'qwk_conf' : sub.qwk_conf,
-                    'qwk_tagline' : sub.qwknet_tagline,
-                    'newsgroup' : sub.newsgroup,
-                    'ars' : sub.ars,
-                    'read_ars' : sub.read_ars,
-                    'can_read' : sub.can_read,
-                    'post_ars' : sub.post_ars,
-                    'can_post' : sub.can_post,
-                    'operator_ars' : sub.operator_ars,
-                    'is_operator' : sub.is_operator,
-                    'moderated_ars' : sub.moderated_ars,
-                    'is_moderated' : sub.is_moderated,
-                    'scan_ptr' : sub.scan_ptr,
-                    'scan_cfg' : sub.scan_cfg
+                {   index : sub.index,
+                    code : sub.code,
+                    grp_index : sub.grp_index,
+                    grp_name : sub.grp_name,
+                    name : sub.name,
+                    description : sub.description,
+                    qwk_name : sub.qwk_name,
+                    qwk_conf : sub.qwk_conf,
+                    qwk_tagline : sub.qwknet_tagline,
+                    newsgroup : sub.newsgroup,
+                    ars : sub.ars,
+                    read_ars : sub.read_ars,
+                    can_read : sub.can_read,
+                    post_ars : sub.post_ars,
+                    can_post : sub.can_post,
+                    operator_ars : sub.operator_ars,
+                    is_operator : sub.is_operator,
+                    moderated_ars : sub.moderated_ars,
+                    is_moderated : sub.is_moderated,
+                    scan_ptr : sub.scan_ptr,
+                    scan_cfg : sub.scan_cfg
                 }
             );
         }
@@ -55,10 +54,10 @@ function listSubs (group) {
     return response;
 }
 
-function getSubUnreadCount (sub) {
+function getSubUnreadCount(sub) {
     var ret = {
-        'scanned' : 0,
-        'total' : 0
+        scanned : 0,
+        total : 0
     };
     if (typeof msg_area.sub[sub] === 'undefined') return ret;
     try {
@@ -67,16 +66,12 @@ function getSubUnreadCount (sub) {
         for (var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
             var i = msgBase.get_msg_index(m);
             if (i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP) continue;
-            if ((   (msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY)
-                    &&
-                    i.to === crc16_calc(user.alias.toLowerCase())
-                    ||
-                    i.to === crc16_calc(user.name.toLowerCase())
-                    ||
+            if ((   msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY &&
+                    i.to === crc16_calc(user.alias.toLowerCase()) ||
+                    i.to === crc16_calc(user.name.toLowerCase()) ||
                     (sub === 'mail' && i.to === crc16_calc(user.number))
-                )
-                ||
-                (msg_area.sub[sub].scan_cfg&SCAN_CFG_NEW)
+                ) ||
+                msg_area.sub[sub].scan_cfg&SCAN_CFG_NEW
             ) {
                 ret.scanned++;
             }
@@ -89,10 +84,10 @@ function getSubUnreadCount (sub) {
     return ret;
 }
 
-function getGroupUnreadCount (group) {
+function getGroupUnreadCount(group) {
     var ret = {
-        'scanned' : 0,
-        'total' : 0
+        scanned : 0,
+        total : 0
     };
     if (typeof msg_area.grp_list[group] === 'undefined') return count;
     msg_area.grp_list[group].sub_list.forEach(
@@ -105,11 +100,10 @@ function getGroupUnreadCount (group) {
     return ret;
 }
 
-function getUnreadInThread (sub, thread) {
+function getUnreadInThread(sub, thread) {
     if (typeof thread === 'number') {
         var threads = getMessageThreads(sub);
-        if(typeof threads.thread[thread] === 'undefined')
-            return 0;
+        if (typeof threads.thread[thread] === 'undefined') return 0;
         thread = threads.thread[thread];
     }
     var count = 0;
@@ -121,7 +115,7 @@ function getUnreadInThread (sub, thread) {
     return count;
 }
 
-function getMailUnreadCount () {
+function getMailUnreadCount() {
     var count = 0;
     var msgBase = new MsgBase('mail');
     msgBase.open();
@@ -137,7 +131,7 @@ function getMailUnreadCount () {
     return count;
 }
 
-function getMailHeaders (sent, ascending) {
+function getMailHeaders(sent, ascending) {
     if (typeof sent !== 'undefined' &&
         sent &&
         user.security.restrictions&UFLAG_K
@@ -167,12 +161,12 @@ function getMailHeaders (sent, ascending) {
     return headers;
 }
 
-function mimeDecode (header, body, code) {
+function mimeDecode(header, body, code) {
     var ret = {
-        'type' : "",
-        'body' : [],
-        'inlines' : [],
-        'attachments' : []
+        type : '',
+        body : [],
+        inlines : [],
+        attachments : []
     };
     var msg = mime_decode(header, body, code);
     if (typeof msg.inlines !== 'undefined') {
@@ -204,13 +198,13 @@ function mimeDecode (header, body, code) {
     return ret;
 }
 
-function getMailBody (number) {
+function getMailBody(number) {
 
     var ret = {
-        'type' : '',
-        'body' : '',
-        'inlines' : [],
-        'attachments' : []
+        type : '',
+        body : '',
+        inlines : [],
+        attachments : []
     };
 
     number = Number(number);
@@ -243,9 +237,9 @@ function getMailBody (number) {
 }
 
 // Returns the user's signature, or an empty String
-function getSignature () {
+function getSignature() {
     var fn = format('%s/user/%04d.sig', system.data_dir, user.number);
-    if (!file_exists(fn)) return "";
+    if (!file_exists(fn)) return '';
     var f = new File(fn);
     f.open('r');
     var signature = f.read();
@@ -255,7 +249,7 @@ function getSignature () {
 
 // Post a messge to 'sub'
 // Called by postNew/postReply, not directly
-function postMessage (sub, header, body) {
+function postMessage(sub, header, body) {
     var ret = false;
     if (user.alias === settings.guest ||
         typeof msg_area.sub[sub] === 'undefined' ||
@@ -287,7 +281,7 @@ function postMessage (sub, header, body) {
 
 // Post a message to the mail sub, if this user can do so
 // Called by postNew/postReply, not directly
-function postMail (header, body) {
+function postMail(header, body) {
     // Lazy ARS checks; we could check the *type* of email being sent, I guess.
     if (user.security.restrictions&UFLAG_E ||
         user.security.restrictions&UFLAG_M
@@ -319,7 +313,7 @@ function postMail (header, body) {
 }
 
 // Post a new (non-reply) message to 'sub'
-function postNew (sub, to, subject, body) {
+function postNew(sub, to, subject, body) {
     if (typeof sub !== 'string' ||
         typeof to !== 'string' ||
         to === '' ||
@@ -344,7 +338,7 @@ function postNew (sub, to, subject, body) {
 }
 
 // Add a new message to 'sub' in reply to parent message 'pid'
-function postReply (sub, body, pid) {
+function postReply(sub, body, pid) {
     var ret = false;
     if (    typeof sub !== 'string' ||
             typeof body !== 'string' ||
@@ -379,7 +373,7 @@ function postReply (sub, body, pid) {
 // Delete a message if
 // - This is the mail sub, and the message was sent by or to this user
 // - This is another sub on which the user is an operator
-function deleteMessage (sub, number) {
+function deleteMessage(sub, number) {
     number = parseInt(number);
     if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail') {
         return false;
@@ -403,7 +397,7 @@ function deleteMessage (sub, number) {
 }
 
 // Deuce's URL-ifier
-function linkify (body) {
+function linkify(body) {
     urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
     body = body.replace(
         urlRE, 
@@ -420,7 +414,7 @@ function linkify (body) {
 }
 
 // Somewhat modified version of Deuce's "magical quoting stuff" from v3
-function quotify (body) {
+function quotify(body) {
 
     var blockquote_start = '<blockquote>';
     var blockquote_end = '</blockquote>';
@@ -507,7 +501,7 @@ function quotify (body) {
 }
 
 // Format message body for the web
-function formatMessage (body, ansi) {
+function formatMessage(body, ansi) {
 
     // Workaround for html_encode(body, true, false, false, false);
     // which causes a crash if body is empty
@@ -564,7 +558,7 @@ function formatMessage (body, ansi) {
 
 }
 
-function setScanCfg (sub, cfg) {
+function setScanCfg(sub, cfg) {
 
     var opts = [
         0,
-- 
GitLab


From 6ae6f407732ca78c47076c12a5ea366fefef2dda Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 17:15:38 -0500
Subject: [PATCH 065/752] Log levels

---
 web/lib/auth.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 23f5fa3d88..8e962386bf 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -117,7 +117,9 @@ function destroySession(cookies) {
 			break;
 
 		} catch (err) {
-			log('Error destroying session: ' + err + ', cookie: ' + cookies[c]);
+			log(LOG_DEBUG,
+				'Error destroying session: ' + err + ', cookie: ' + cookies[c]
+			);
 		}
 
 	}
-- 
GitLab


From 523bee45a629847c2a239922e132a47d384c17d1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 17:15:58 -0500
Subject: [PATCH 066/752] Log levels

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index ec8697293b..3566d296a1 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -79,7 +79,7 @@ function getSubUnreadCount(sub) {
         }
         msgBase.close();
     } catch (err) {
-        log(err);
+        log(LOG_ERR, err);
     }
     return ret;
 }
-- 
GitLab


From 40b1011d19d9df21f9f0d4b543cd817de84c6377 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 17:18:00 -0500
Subject: [PATCH 067/752] Log levels

---
 web/lib/pages.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index b357991efc..0a13db9978 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -1,8 +1,10 @@
+load('sbbsdefs.js');
+
 function getWebCtrl() {
 	if (!file_exists(settings.web_root + 'pages/webctrl.ini')) return false;
 	var f = new File(settings.web_root + 'pages/webctrl.ini');
 	if (!f.open('r')) {
-		log('Unable to open pages/webctrl.ini');
+		log(LOG_ERR, 'Unable to open pages/webctrl.ini');
 		exit();
 	}
 	var ini = f.iniGetAllObjects();
@@ -14,8 +16,7 @@ function webCtrlTest(ini, filename) {
 	var ret = true;
 	for (var i = 0; i < ini.length; i++) {
 		if (!wildmatch(false, filename, ini[i].name)) continue;
-		if (typeof ini[i].AccessRequirements === 'undefined'
-			||
+		if (typeof ini[i].AccessRequirements === 'undefined' ||
 			user.compare_ars(ini[i].AccessRequirements)
 		) {
 			continue;
-- 
GitLab


From 2cbabcaa93ea339a1f7b301c969aed57ea27d598 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 17:20:29 -0500
Subject: [PATCH 068/752] Cleanup

---
 web/lib/sidebar.js | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index d9694aa19d..52c11e54b8 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -2,9 +2,8 @@ function getSidebarModules() {
 	var sidebarModules = [];
 	var d = directory(settings.web_root + 'sidebar/*');
 	d.forEach(
-		function(item) {
-			if(file_isdir(item))
-				return;
+		function (item) {
+			if (file_isdir(item)) return;
 			var fn = file_getname(item);
 			// Check webctrl.ini
 			sidebarModules.push(fn);
-- 
GitLab


From e1c1eaea38c22d8f71802b26316b0c0cb4c2ce1a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 17:43:29 -0500
Subject: [PATCH 069/752] Reduced inline XJS for greater readability.

---
 web/root/index.xjs | 268 +++++++++++++++++++++++----------------------
 1 file changed, 140 insertions(+), 128 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index adf9d43b68..c78aa17ba2 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,4 +1,5 @@
 <?xjs
+
 	load('xjs.js');
 	load(system.exec_dir + '../web/lib/init.js');
 	load(settings.web_lib + 'auth.js');
@@ -16,6 +17,141 @@
 	} else {
 		var page = file_getname(http_request.query.page[0]);
 	}
+
+	function writeTitle() {
+		writeln(
+			getPageTitle(
+				settings.web_root + 'pages/' + page
+			).replace(/^HIDDEN(\:)*/, '')
+		);
+	}
+
+	function writeNavBar() {
+		var pages = getPages();
+		var primary = [];
+		var secondary = [];
+		pages.forEach(
+			function (thePage) {
+				if (thePage.primary) {
+					primary.push(thePage);
+				} else {
+					secondary.push(thePage);
+				}
+			}
+		);
+		primary.forEach(
+			function (p) {
+				writeln(
+					'<li' + (p.page === page ? ' class="active"' : '') + '>' +
+					'<a href="./?page=' + p.page + '">' + p.title +	'</a></li>'
+				);
+			}
+		);
+		if (secondary.length > 0) {
+			writeln(
+				'<li class="dropdown">' +
+				'<a	href="#" ' +
+					'class="dropdown-toggle" ' +
+					'data-toggle="dropdown" ' +
+					'role="button" aria-haspopup="true" ' +
+					'aria-expanded="false">' +
+					'More<span class="caret"></span>' +
+				'</a>' +
+				'<ul class="dropdown-menu">'
+			);
+			secondary.forEach(
+				function (p) {
+					writeln(
+						'<li' +	(p.page === page ? ' class="active"' : '') + '>' +
+						'<a href="./?page=' + p.page + '">' +
+							p.title +
+						'</a>' +
+						'</li>'
+					);
+				}
+			);
+			writeln('</ul></li>');
+		}
+	}
+
+	function writeAuthMenu() {
+		if (user.alias === settings.guest || user.number < 1) {
+			if (settings.user_registration) {
+				writeln(
+					'<li><a href="./?page=000-register.xjs">Register</a></li>'
+				);
+			}
+			writeln(
+				'<li class="dropdown">' +
+				'<a	href="#" ' +
+					'class="dropdown-toggle" ' +
+					'data-toggle="dropdown" ' +
+					'role="button" ' +
+					'aria-haspopup="true" ' +
+					'aria-expanded="false">' +
+				'Log in<span class="caret"></span></a>' +
+				'<div id="login-form" ' +
+					'class="dropdown-menu" ' +
+					'style="padding:15px; padding-bottom: 0px;"' +
+				'>' +
+				'<form id="form-login">' +
+				'<label for="input-username" class="sr-only">Username</label>' +
+				'<input id="input-username" ' +
+					'title="Username" ' +
+					'type="text" ' +
+					'class="dropdown form-control" ' +
+					'placeholder="Username"' +
+				'>' +
+				'<label for="input-password" class="sr-only">Password</label>' +
+				'<input id="input-password" ' +
+					'title="Password" ' +
+					'type="password" ' +
+					'class="dropdown form-control" ' +
+					'placeholder="Password"' +
+				'>' +
+				'<input id="button-login" ' +
+					'class="dropdown btn btn-primary" ' +
+					'type="submit" ' +
+					'value="Log in"' +
+					'>' +
+				'</form>' +
+				'</div>' +
+				'</li>'
+			);
+		} else {
+			writeln(
+				'<li class="dropdown">' +
+				'<a	href="#" ' +
+					'class="dropdown-toggle" ' +
+					'data-toggle="dropdown" ' +
+					'role="button" ' +
+					'aria-haspopup="true" ' +
+					'aria-expanded="false">' +
+					user.alias + 
+					' <span class="badge scanned" title="Unread mail" ' +
+					'id="badge-unread-mail"></span>' +
+					' <span class="caret"></span>' +
+				'</a>' +
+				'<ul class="dropdown-menu">' +
+				'<li><a href="./?page=000-mail.ssjs">Mail ' +
+				'<span class="badge scanned" ' +
+					'title="Unread mail" ' +
+					'id="badge-unread-mail-inner">' +
+				'</span>' +
+				'</a></li>' +
+				'<li><a id="button-logout" href="#">Log out</a></li>' +
+				'</ul>'
+			);
+		}
+	}
+
+	function writePage() {
+		var ini = getWebCtrl();
+		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
+			write(getPage(page));
+		}
+	}
+
 ?>
 
 
@@ -26,17 +162,7 @@
 		<meta http-equiv="X-UA-Compatible" content="IE=edge">
 		<meta name="viewport" content="width=device-width, initial-scale=1">
 		<link rel="icon" href="./images/favicon.ico">
-		<title>
-			<?xjs 
-				write(
-					getPageTitle(
-						settings.web_root + 'pages/' + page
-					).replace(/^HIDDEN(\:)*/, '')
-				);
-			?>
-			:
-			<?xjs write(system.name); ?>
-		</title>
+		<title><?xjs writeTitle(); ?> :	<?xjs write(system.name); ?></title>
 		<link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
 		<link href="./css/offcanvas.css" rel="stylesheet">
 		<link href="./css/style.css" rel="stylesheet">
@@ -77,116 +203,9 @@
 					<a class="navbar-brand" href="./"><?xjs write(system.name); ?></a>
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
-					<ul class="nav navbar-nav">
-						<?xjs 
-							var pages = getPages();
-							var primary = [];
-							var secondary = [];
-							pages.forEach(
-								function (thePage) {
-									if (thePage.primary) {
-										primary.push(thePage);
-									} else {
-										secondary.push(thePage);
-									}
-								}
-							);
-							pages = [];
-							primary.forEach(
-								function (thePage) {
-									writeln(
-										'<li' +
-										(thePage.page === page
-											? ' class="active"' : "") + 
-										'>' +
-										'<a href="./?page=' + thePage.page + '">' +
-										thePage.title +
-										'</a></li>'
-									);
-								}
-							);
-							if (secondary.length > 0) {
-								writeln(
-									'<li class="dropdown">' +
-									'<a	href="#" ' +
-										'class="dropdown-toggle" ' +
-										'data-toggle="dropdown" ' +
-										'role="button" ' +
-										'aria-haspopup="true" ' +
-										'aria-expanded="false">' +
-									'More<span class="caret"></span></a>' +
-									'<ul class="dropdown-menu">'
-								);
-								secondary.forEach(
-									function (thePage) {
-										writeln(
-											'<li' +
-											(thePage.page === page
-												? ' class="active"' : "") + 
-											'>' +
-											'<a href="./?page=' + thePage.page + '">' +
-											thePage.title +
-											'</a></li>'
-										);
-									}
-								);
-								writeln('</ul></li>');
-							}
-						?>
-					</ul>
+					<ul class="nav navbar-nav"><?xjs writeNavBar(); ?></ul>
 					<ul class="nav navbar-nav navbar-right">
-					<?xjs
-					if (user.alias === settings.guest || user.number < 1) {
-						if (settings.user_registration) {
-							writeln(
-								'<li>' +
-								'<a href="./?page=000-register.xjs">Register</a>' +
-								'</li>'
-							);
-						}
-						writeln(
-							'<li class="dropdown">' +
-							'<a	href="#" ' +
-								'class="dropdown-toggle" ' +
-								'data-toggle="dropdown" ' +
-								'role="button" ' +
-								'aria-haspopup="true" ' +
-								'aria-expanded="false">' +
-							'Log in<span class="caret"></span></a>' +
-							'<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom: 0px;">' +
-							'<form id="form-login">' +
-							'<label for="input-username" class="sr-only">Username</label>' +
-							'<input id="input-username" title="Username" type="text" class="dropdown form-control" placeholder="Username">' +
-							'<label for="input-password" class="sr-only">Password</label>' +
-							'<input id="input-password" title="Password" type="password" class="dropdown form-control" placeholder="Password">' +
-							'<input id="button-login" class="dropdown btn btn-primary" type="submit" value="Log in">' +
-							'</form>' +
-							'</div>' +
-							'</li>'
-						);
-					} else {
-						writeln(
-							'<li class="dropdown">' +
-							'<a	href="#" ' +
-								'class="dropdown-toggle" ' +
-								'data-toggle="dropdown" ' +
-								'role="button" ' +
-								'aria-haspopup="true" ' +
-								'aria-expanded="false">' +
-								user.alias + 
-								' <span class="badge scanned" title="Unread mail" ' +
-								'id="badge-unread-mail"></span>' +
-								' <span class="caret"></span>' +
-							'</a>' +
-							'<ul class="dropdown-menu">' +
-							'<li><a href="./?page=000-mail.ssjs">Mail ' +
-							'<span class="badge scanned" title="Unread mail" id="badge-unread-mail-inner"></span>' +
-							'</a></li>' +
-							'<li><a id="button-logout" href="#">Log out</a></li>' +
-							'</ul>'
-						);
-					}
-					?>
+						<?xjs writeAuthMenu(); ?>
 					</ul>
 				</div>
 		  </div>
@@ -202,14 +221,7 @@
 							<span class="glyphicon glyphicon-tasks"></span> Sidebar
 						</button>
 					</p>
-					<?xjs 
-						var ini = getWebCtrl();
-						if ((typeof ini == "boolean" && !ini) ||
-							webCtrlTest(ini, page)
-						) {
-							write(getPage(page));
-						}
-					?>
+					<?xjs writePage(); ?>
 				</div>
 
 				<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
-- 
GitLab


From 1ec4bab9fc5e941bcb82ab4847e29c0787f29eb6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 15 Dec 2015 23:31:52 -0500
Subject: [PATCH 070/752] Stop doing keyboard thread navigation when composing
 a message.

---
 web/root/js/forum.js | 63 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 7a3eb90415..aa4858086f 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -118,6 +118,17 @@ function addNew(sub) {
 		}
 	);
 	window.location.hash = '#newmessage';
+	$('#newmessage-body').focus(
+		function () {
+			$(window).off('keydown');
+		}
+	);
+	$('#newmessage-body').blur(
+		function () {
+			window.location.hash = '';
+			threadNav();
+		}
+	);
 }
 
 // Add a reply input form to the page for message with number 'id' in sub 'sub'
@@ -139,6 +150,12 @@ function addReply(sub, id) {
 			);
 		}
 	);
+	$('#replytext-' + id).focus(
+		function () {
+			$(window).off('keydown');
+		}
+	);
+	$('#replytext-' + id).blur(threadNav);
 }
 
 // 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
@@ -240,3 +257,49 @@ function setScanCfg(sub, cfg) {
 		}
 	);
 }
+
+function threadNav() {
+
+	if (window.location.hash === '') {
+		$($('#forum-list-container').children('.list-group-item')[0]).addClass(
+			'current'
+		);
+	} else if ($('#li-' + window.location.hash.substr(1)).length > 0) {
+		$('#li-' + window.location.hash.substr(1)).addClass('current');
+	}
+
+	$(window).keydown(
+		function (evt) {
+			var cid = $(
+				$('#forum-list-container').children('.current')[0]
+			).attr(
+				'id'
+			).substr(3);
+			switch (evt.keyCode) {
+				case 37:
+					// Left
+					window.location.hash = $('#pm-' + cid).attr('href');
+					break;
+				case 39:
+					// Right
+					window.location.hash = $('#nm-' + cid).attr('href');
+					break;
+				default:
+					break;
+			}
+		}
+	);
+
+	$(window).on(
+		'hashchange',
+		function () {
+			$('#forum-list-container').children('.current').removeClass(
+				'current'
+			);
+			var id = window.location.hash.substr(1);
+			if ($('#li-' + id).length < 1) return;
+			$('#li-' + id).addClass('current');
+		}
+	);
+
+}
\ No newline at end of file
-- 
GitLab


From 662e4c5355021c432eb3c104bd804f2723760f8c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Dec 2015 00:09:51 -0500
Subject: [PATCH 071/752] Log levels

---
 web/root/api/attachments.ssjs | 3 ++-
 web/root/api/register.ssjs    | 5 +++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index 51cf998ab8..cd3777acf9 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -1,9 +1,10 @@
+load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'mime-decode.js');
 
 function barfOut(err) {
-	log(err);
+	log(LOG_WARNING, err);
 	exit();
 }
 
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 992c4d587d..3bd2e5d231 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -69,7 +69,7 @@ function newUser() {
 		reply.errors.push('Failed to create user record.');
 		return;
 	}
-	log('User #' + usr.number + ' registered via HTTP.');
+	log(LOG_INFO, 'User #' + usr.number + ' registered via HTTP.');
 	usr.security.password = prepUser.password;
 	for (var property in prepUser) {
 		if (property === 'alias' || property === 'password') continue;
@@ -86,7 +86,8 @@ if ((	paramExists('send-me-free-stuff') &&
 		http_request.query['subscribe-to-newsletter'][0] !== ''
 	)
 ) {
-	log('Hidden registration form input element filled.  ' +
+	log(LOG_WARNING,
+		'Hidden registration form input element filled.  ' +
 		'Likely a bot.  Cancelling user registration.'
 	);
 	exit();
-- 
GitLab


From d2efa54826cccff07b8af09ffb7a4191f4941bd8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Dec 2015 01:32:10 -0500
Subject: [PATCH 072/752] Cleanup

---
 mods/websocket-proxy.js          | 288 ++++++++++++++++++++-----------
 mods/websocket-rlogin-service.js |   8 +-
 2 files changed, 194 insertions(+), 102 deletions(-)

diff --git a/mods/websocket-proxy.js b/mods/websocket-proxy.js
index d68d017a5f..0d0d34e1ea 100644
--- a/mods/websocket-proxy.js
+++ b/mods/websocket-proxy.js
@@ -1,4 +1,4 @@
-load("sha1.js");
+load('sha1.js');
 
 var WebSocketProxy = function(client) {
 
@@ -20,10 +20,10 @@ var WebSocketProxy = function(client) {
 	var ServerDataBuffer = []; // From server
 
 	function CalculateWebSocketKey(InLine) {
-		var Digits = "";
+		var Digits = '';
 		var Spaces = 0;
 		for (var i = 0; i < InLine.length; i++) {
-			if (InLine.charAt(i) == " ") {
+			if (InLine.charAt(i) == ' ') {
 				Spaces++;
 			} else if (!isNaN(InLine.charAt(i))) {
 				Digits += InLine.charAt(i);
@@ -60,7 +60,8 @@ var WebSocketProxy = function(client) {
 					}
 					break;
 				case WEBSOCKET_DATA:
-					// We're in a data packet, so check for 0xFF, which indicates the data packet is done
+					// We're in a data packet, so check for 0xFF, which
+					// indicates the data packet is done
 					if (InByte == 0xFF) {
 						FWebSocketState = WEBSOCKET_NEED_PACKET_START;
 					} else {
@@ -72,10 +73,13 @@ var WebSocketProxy = function(client) {
 							InByte2 = client.socket.recvBin(1);
 							Result.push(((InByte & 31) << 6) | (InByte2 & 63));
 						} else {
-							// Handle UTF-8 decode (should never need this, but included anyway)
+							// Handle UTF-8 decode (should never need this, but
+							// included anyway)
 							InByte2 = client.socket.recvBin(1);
 							InByte3 = client.socket.recvBin(1);
-							Result.push(((InByte & 15) << 12) | ((InByte2 & 63) << 6) | (InByte3 & 63));
+							Result.push(
+								((InByte&15)<<12)|((InByte2&63)<<6)|(InByte3&63)
+							);
 						}
 					}
 					break;
@@ -95,7 +99,8 @@ var WebSocketProxy = function(client) {
 			// Check what the client packet state is
 			switch (FWebSocketState) {
 				case WEBSOCKET_NEED_PACKET_START:
-					// Next byte will give us the opcode, and also tell is if the message is fragmented
+					// Next byte will give us the opcode, and also tell is if
+					// the message is fragmented
 					FFrameMask = [];
 					FFrameOpCode = client.socket.recvBin(1);
 					FFramePayloadLength = 0;
@@ -120,21 +125,31 @@ var WebSocketProxy = function(client) {
 					FWebSocketState = WEBSOCKET_DATA;
 					break;
 				case WEBSOCKET_DATA:
-					InByte = (client.socket.recvBin(1) ^ FFrameMask[FFramePayloadReceived++ % 4]);
+					InByte = (
+						client.socket.recvBin(1)^FFrameMask[
+							FFramePayloadReceived++ % 4
+						]
+					);
 
 					// Check if the byte needs to be UTF-8 decoded
 					if ((InByte & 0x80) === 0) {
 						Result.push(InByte);
 					} else if ((InByte & 0xE0) === 0xC0) {
 						// Handle UTF-8 decode
-						InByte2 = (client.socket.recvBin(1) ^ FFrameMask[FFramePayloadReceived++ % 4]);
+						InByte2 = (
+							client.socket.recvBin(1)^FFrameMask[
+								FFramePayloadReceived++ % 4
+							]
+						);
 						Result.push(((InByte & 31) << 6) | (InByte2 & 63));
 					} else {
-						log(LOG_ERR, "Byte out of range: " + InByte);
+						log(LOG_NOTICE, 'Byte out of range: ' + InByte);
 					}
 
 					// Check if we've received the full payload
-					if (FFramePayloadReceived === FFramePayloadLength) FWebSocketState = WEBSOCKET_NEED_PACKET_START;
+					if (FFramePayloadReceived === FFramePayloadLength) {
+						FWebSocketState = WEBSOCKET_NEED_PACKET_START;
+					}
 					break;
 			}
 		}
@@ -168,7 +183,7 @@ var WebSocketProxy = function(client) {
 				client.socket.sendBin((AData[i] >> 6) | 192, 1);
 				client.socket.sendBin((AData[i] & 63) | 128, 1);
 			} else {
-				log(LOG_ERR, "Byte out of range: " + AData[i]);
+				log(LOG_NOTICE, 'Byte out of range: ' + AData[i]);
 			}
 		}
 
@@ -184,12 +199,14 @@ var WebSocketProxy = function(client) {
 				// Check if the byte needs to be UTF-8 encoded
 				if ((AData[i] & 0xFF) <= 127) {
 					ToSend.push(AData[i]);
-				} else if (((AData[i] & 0xFF) >= 128) && ((AData[i] & 0xFF) <= 2047)) {
+				} else if (
+					((AData[i] & 0xFF) >= 128) && ((AData[i] & 0xFF) <= 2047)
+				) {
 					// Handle UTF-8 encode
 					ToSend.push((AData[i] >> 6) | 192);
 					ToSend.push((AData[i] & 63) | 128);
 				} else {
-					log(LOG_ERR, "Byte out of range: " + AData[i]);
+					log(LOG_NOTICE, 'Byte out of range: ' + AData[i]);
 				}
 			}
 
@@ -200,9 +217,10 @@ var WebSocketProxy = function(client) {
 				client.socket.sendBin(126, 1);
 				client.socket.sendBin(ToSend.length, 2);
 			} else {
-				// NOTE: client.socket.sendBin(ToSend.length, 8); didn't work, so this
-				//       modification limits the send to 2^32 bytes (probably not an issue)
-				//       Probably should look into a proper fix at some point though
+				// NOTE: client.socket.sendBin(ToSend.length, 8); didn't work,
+				// so this modification limits the send to 2^32 bytes (probably
+				// not an issue). Probably should look into a proper fix at
+				// some point though
 				client.socket.sendBin(127, 1);
 				client.socket.sendBin(0, 4);
 				client.socket.sendBin(ToSend.length, 4);
@@ -220,17 +238,20 @@ var WebSocketProxy = function(client) {
 		try {
 			// Keep reading header data until we get all the data we want
 			while (true) {
-				// Read another line, and abort if we don't get one within 5 seconds
+				// Read another line, abort if we don't get one in 5 seconds
 				var InLine = client.socket.recvline(1024, 5);
 				if (InLine === null) {
-					log(LOG_ERR, "Timeout exceeded while waiting for complete handshake");
+					log(LOG_ERR,
+						'Timeout exceeded while waiting for complete handshake'
+					);
 					return false;
 				}
 
-				log(LOG_DEBUG, "Handshake Line: " + InLine);
+				log(LOG_DEBUG, 'Handshake Line: ' + InLine);
 				
-				// Check for blank line (indicates we have most of the header, and only the last 8 bytes remain
-				if (InLine === "") {
+				// Check for blank line (indicates we have most of the header,
+				// and only the last 8 bytes remain
+				if (InLine === '') {
 					switch (self.headers['Version']) {
 						case 0: 
 							return ShakeHandsDraft0();
@@ -239,67 +260,88 @@ var WebSocketProxy = function(client) {
 						case 13:
 							return ShakeHandsVersion7();
 						default: 
-							//		    TODO If this version does not
-							//          match a version understood by the server, the server MUST
-							//          abort the websocket handshake described in this section and
-							//          instead send an appropriate HTTP error code (such as 426
-							//          Upgrade Required), and a |Sec-WebSocket-Version| header
-							//          indicating the version(s) the server is capable of
-							//          understanding.
+							// TODO If this version does not match a version
+							// understood by the server, the server MUST abort
+							// the websocket handshake described in this section
+							// and instead send an appropriate HTTP error code
+							// (such as 426 Upgrade Required), and a
+							// |Sec-WebSocket-Version| header indicating the
+							// version(s) the server is capable of
+							// understanding.
 							break;
 					}
 					break;
-				} else if (InLine.indexOf("Connection:") === 0) {
+				} else if (InLine.indexOf('Connection:') === 0) {
 					// Example: "Connection: Upgrade"
-					self.headers['Connection'] = InLine.replace(/Connection:\s?/i, "");
-				} else if (InLine.indexOf("GET") === 0) {
+					self.headers['Connection'] = InLine.replace(
+						/Connection:\s?/i,
+						''
+					);
+				} else if (InLine.indexOf('GET') === 0) {
 					// Example: "GET /demo HTTP/1.1"
-					var GET = InLine.split(" ");
+					var GET = InLine.split(' ');
 					self.headers['Path'] = GET[1];
-				} else if (InLine.indexOf("Host:") === 0) {
+				} else if (InLine.indexOf('Host:') === 0) {
 					// Example: "Host: example.com"
-					self.headers['Host'] = InLine.replace(/Host:\s?/i, "");
-				} else if (InLine.indexOf("Origin:") === 0) {
+					self.headers['Host'] = InLine.replace(/Host:\s?/i, '');
+				} else if (InLine.indexOf('Origin:') === 0) {
 					// Example: "Origin: http://example.com"
-					self.headers['Origin'] = InLine.replace(/Origin:\s?/i, "");
-				} else if (InLine.indexOf("Sec-WebSocket-Key:") === 0) {
+					self.headers['Origin'] = InLine.replace(/Origin:\s?/i, '');
+				} else if (InLine.indexOf('Sec-WebSocket-Key:') === 0) {
 					// Example: "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=="
-					self.headers['Key'] = InLine.replace(/Sec-WebSocket-Key:\s?/i, "");
-				} else if (InLine.indexOf("Sec-WebSocket-Key1:") === 0) {
+					self.headers['Key'] = InLine.replace(
+						/Sec-WebSocket-Key:\s?/i,
+						''
+					);
+				} else if (InLine.indexOf('Sec-WebSocket-Key1:') === 0) {
 					// Example: "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5"
-					self.headers['Key1'] = CalculateWebSocketKey(InLine.replace(/Sec-WebSocket-Key1:\s?/i, ""));
-				} else if (InLine.indexOf("Sec-WebSocket-Key2:") === 0) {
+					self.headers['Key1'] = CalculateWebSocketKey(
+						InLine.replace(/Sec-WebSocket-Key1:\s?/i, '')
+					);
+				} else if (InLine.indexOf('Sec-WebSocket-Key2:') === 0) {
 					// Example: "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00"
-					self.headers['Key2'] = CalculateWebSocketKey(InLine.replace(/Sec-WebSocket-Key2:\s?/i, ""));
-				} else if (InLine.indexOf("Sec-WebSocket-Origin:") === 0) {
+					self.headers['Key2'] = CalculateWebSocketKey(
+						InLine.replace(/Sec-WebSocket-Key2:\s?/i, '')
+					);
+				} else if (InLine.indexOf('Sec-WebSocket-Origin:') === 0) {
 					// Example: "Sec-WebSocket-Origin: http://example.com"
-					self.headers['Origin'] = InLine.replace(/Sec-WebSocket-Origin:\s?/i, "");
-				} else if (InLine.indexOf("Sec-WebSocket-Protocol:") === 0) {
+					self.headers['Origin'] = InLine.replace(
+						/Sec-WebSocket-Origin:\s?/i,
+						''
+					);
+				} else if (InLine.indexOf('Sec-WebSocket-Protocol:') === 0) {
 					// Example: "Sec-WebSocket-Protocol: sample"
-					self.headers['SubProtocol'] = InLine.replace(/Sec-WebSocket-Protocol:\s?/i, "");
-				} else if (InLine.indexOf("Sec-WebSocket-Draft") === 0) {
+					self.headers['SubProtocol'] = InLine.replace(
+						/Sec-WebSocket-Protocol:\s?/i,
+						''
+					);
+				} else if (InLine.indexOf('Sec-WebSocket-Draft') === 0) {
 					// Example: "Sec-WebSocket-Draft: 2"
 					try {
-						self.headers['Version'] = parseInt(InLine.replace(/Sec-WebSocket-Draft:\s?/i, ""));
+						self.headers['Version'] = parseInt(
+							InLine.replace(/Sec-WebSocket-Draft:\s?/i, '')
+						);
 					} catch (err) {
 						self.headers['Version'] = 0;
 					}
-				} else if (InLine.indexOf("Sec-WebSocket-Version") === 0) {
+				} else if (InLine.indexOf('Sec-WebSocket-Version') === 0) {
 					// Example: "Sec-WebSocket-Version: 8"
 					try {
-						self.headers['Version'] = parseInt(InLine.replace(/Sec-WebSocket-Version:\s?/i, ""));
+						self.headers['Version'] = parseInt(
+							InLine.replace(/Sec-WebSocket-Version:\s?/i, '')
+						);
 					} catch (err) {
 						self.headers['Version'] = 0;
 					}
-				} else if (InLine.indexOf("Upgrade:") === 0) {
+				} else if (InLine.indexOf('Upgrade:') === 0) {
 					// Example: "Upgrade: websocket"
-					self.headers['Upgrade'] = InLine.replace(/Upgrade:\s?/i, "");
-				} else if (InLine.indexOf("Cookie:") === 0) {
-				 	self.headers['Cookie'] = InLine.replace(/Cookie:\s?/i, "");
+					self.headers['Upgrade'] = InLine.replace(/Upgrade:\s?/i,'');
+				} else if (InLine.indexOf('Cookie:') === 0) {
+				 	self.headers['Cookie'] = InLine.replace(/Cookie:\s?/i, '');
 				}
 			}
 		} catch (err) {
-			log(LOG_ERR, "ShakeHands() error: " + err.toString());
+			log(LOG_ERR, 'ShakeHands() error: ' + err.toString());
 		}
 		
 		return false;
@@ -307,17 +349,39 @@ var WebSocketProxy = function(client) {
 
 	function ShakeHandsDraft0() {
 		// Ensure we have all the data we need
-		if (('Key1' in self.headers) && ('Key2' in self.headers) && ('Host' in self.headers) && ('Origin' in self.headers !== "") && ('Path' in self.headers)) {
-			// Combine Key1, Key2, and the last 8 bytes into a string that we will later hash
-			var ToHash = ""
-			ToHash += String.fromCharCode((self.headers['Key1'] & 0xFF000000) >> 24);
-			ToHash += String.fromCharCode((self.headers['Key1'] & 0x00FF0000) >> 16);
-			ToHash += String.fromCharCode((self.headers['Key1'] & 0x0000FF00) >> 8);
-			ToHash += String.fromCharCode((self.headers['Key1'] & 0x000000FF) >> 0);
-			ToHash += String.fromCharCode((self.headers['Key2'] & 0xFF000000) >> 24);
-			ToHash += String.fromCharCode((self.headers['Key2'] & 0x00FF0000) >> 16);
-			ToHash += String.fromCharCode((self.headers['Key2'] & 0x0000FF00) >> 8);
-			ToHash += String.fromCharCode((self.headers['Key2'] & 0x000000FF) >> 0);				
+		if (('Key1' in self.headers) &&
+			('Key2' in self.headers) &&
+			('Host' in self.headers) &&
+			('Origin' in self.headers !== '') &&
+			('Path' in self.headers)
+		) {
+			// Combine Key1, Key2, and the last 8 bytes into a string that we
+			// will later hash
+			var ToHash = ''
+			ToHash += String.fromCharCode(
+				(self.headers['Key1'] & 0xFF000000) >> 24
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key1'] & 0x00FF0000) >> 16
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key1'] & 0x0000FF00) >> 8
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key1'] & 0x000000FF) >> 0
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key2'] & 0xFF000000) >> 24
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key2'] & 0x00FF0000) >> 16
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key2'] & 0x0000FF00) >> 8
+			);
+			ToHash += String.fromCharCode(
+				(self.headers['Key2'] & 0x000000FF) >> 0
+			);
 			for (var i = 0; i < 8; i++) {
 				ToHash += String.fromCharCode(client.socket.recvBin(1));
 			}
@@ -326,17 +390,29 @@ var WebSocketProxy = function(client) {
 			var Hashed = md5_calc(ToHash, true);
 
 			// Setup the handshake response
-			var Response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
-						   "Upgrade: WebSocket\r\n" +
-						   "Connection: Upgrade\r\n" +
-						   "Sec-WebSocket-Origin: " + self.headers['Origin'] + "\r\n" +
-						   "Sec-WebSocket-Location: ws://" + self.headers['Host'] + self.headers['Path'] + "\r\n";
-			if ('SubProtocol' in self.headers) Response += "Sec-WebSocket-Protocol: " + self.headers['SubProtocol'] + "\r\n";
-			Response += "\r\n";
+			var Response = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
+						   'Upgrade: WebSocket\r\n' +
+						   'Connection: Upgrade\r\n' +
+						   'Sec-WebSocket-Origin: ' + self.headers['Origin'] +
+						   '\r\n' +
+						   'Sec-WebSocket-Location: ws://' +
+						   self.headers['Host'] +
+						   self.headers['Path'] +
+						   '\r\n';
+			if ('SubProtocol' in self.headers) {
+				Response +=
+					'Sec-WebSocket-Protocol: ' +
+					self.headers['SubProtocol'] +
+					'\r\n';
+			}
+			Response += '\r\n';
 			
-			// Loop through the hash string (which is hex encoded) and append the individual bytes to the response
+			// Loop through the hash string (which is hex encoded) and append
+			// the individual bytes to the response
 			for (var i = 0; i < Hashed.length; i += 2) {
-				Response += String.fromCharCode(parseInt(Hashed.charAt(i) + Hashed.charAt(i + 1), 16));
+				Response += String.fromCharCode(
+					parseInt(Hashed.charAt(i) + Hashed.charAt(i + 1), 16)
+				);
 			}
 			
 			// Send the response and return
@@ -344,9 +420,11 @@ var WebSocketProxy = function(client) {
 			return true;
 		} else {
 			// We're missing some pice of data, log what we do have
-			log(LOG_ERR, "Missing some piece of handshake data.  Here's what we have:");
+			log(LOG_ERR,
+				'Missing some piece of handshake data.  Here\'s what we have:'
+			);
 			for(var x in self.headers) { 
-				log(LOG_ERR, x + " => " + self.headers[x]); 
+				log(LOG_ERR, x + ' => ' + self.headers[x]); 
 			}
 			return false;
 		}
@@ -354,8 +432,12 @@ var WebSocketProxy = function(client) {
 
 	function ShakeHandsVersion7() {
 		// Ensure we have all the data we need
-		if (('Key' in self.headers) && ('Host' in self.headers) && ('Origin' in self.headers !== "") && ('Path' in self.headers)) {
-			var AcceptGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+		if (('Key' in self.headers) &&
+			('Host' in self.headers) &&
+			('Origin' in self.headers !== '') &&
+			('Path' in self.headers)
+		) {
+			var AcceptGUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
 
 			// Combine Key and GUID
 			var ToHash = self.headers['Key'] + AcceptGUID;
@@ -366,42 +448,48 @@ var WebSocketProxy = function(client) {
 			// Encode the hash
 			var ToEncode = '';
 			for (var i = 0; i <= 38; i += 2) {
-				ToEncode += String.fromCharCode(parseInt(Hashed.substr(i, 2), 16));
+				ToEncode += String.fromCharCode(
+					parseInt(Hashed.substr(i, 2), 16)
+				);
 			}
 			var Encoded = base64_encode(ToEncode);
 
 			// Setup the handshake response
-			var Response = "HTTP/1.1 101 Switching Protocols\r\n" +
-						   "Upgrade: websocket\r\n" +
-						   "Connection: Upgrade\r\n" +
-						   "Sec-WebSocket-Accept: " + Encoded + "\r\n";
-			if ('SubProtocol' in self.headers) Response += "Sec-WebSocket-Protocol: plain\r\n"; // Only sub-protocol we support
-			Response += "\r\n";
+			var Response = 'HTTP/1.1 101 Switching Protocols\r\n' +
+						   'Upgrade: websocket\r\n' +
+						   'Connection: Upgrade\r\n' +
+						   'Sec-WebSocket-Accept: ' + Encoded + '\r\n';
+			if ('SubProtocol' in self.headers) {
+				// Only sub-protocol we support
+				Response += 'Sec-WebSocket-Protocol: plain\r\n';
+			}
+			Response += '\r\n';
 			
 			// Send the response and return
 			client.socket.send(Response);
 			return true;
 		} else {
-			// We're missing some pice of data, log what we do have
-			log(LOG_ERR, "Missing some piece of handshake data.  Here's what we have:");
+			log(LOG_ERR,
+				'Missing some piece of handshake data.  Here\'s what we have:'
+			);
 			for(var x in self.headers) { 
-				log(LOG_ERR, x + " => " + self.headers[x]); 
+				log(LOG_ERR, x + ' => ' + self.headers[x]); 
 			}
 			return false;
 		}
 	}
 
 	this.__defineGetter__(
-		"data_waiting",
+		'data_waiting',
 		function() {
 			return (ClientDataBuffer.length > 0);
 		}
 	);
 
 	this.send = function(data) {
-		if(typeof data == "string") {
-			data = data.split("").map(
-				function(d) {
+		if (typeof data === 'string') {
+			data = data.split('').map(
+				function (d) {
 					return ascii(d);
 				}
 			);
@@ -410,15 +498,18 @@ var WebSocketProxy = function(client) {
 	}
 
 	this.receive = function() {
-		var data = "";
-		while(ClientDataBuffer.length > 0) {
+		var data = '';
+		while (ClientDataBuffer.length > 0) {
 			data += ascii(ClientDataBuffer.shift());
 		}
 		return data;
 	}
 
 	this.receiveArray = function(len) {
-		return ClientDataBuffer.splice(0, (typeof len == "number" ? len : ClientDataBuffer.length));
+		return ClientDataBuffer.splice(
+			0,
+			(typeof len === 'number' ? len : ClientDataBuffer.length)
+		);
 	}
 
 	this.cycle = function() {
@@ -426,7 +517,6 @@ var WebSocketProxy = function(client) {
 		SendToWebSocketClient(ServerDataBuffer.splice(0, 4096));
 	}
 
-	if(!ShakeHands())
-		throw "ShakeHands() failed";
+	if(!ShakeHands()) throw 'ShakeHands() failed';
 
 }
\ No newline at end of file
diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index d14db81a7b..9ba685899b 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -17,8 +17,9 @@ function getSession(un) {
 	return session;
 }
 
-// Obfuscated lazy port of an unfinished node.js rlogin client I made quite some time ago.
-// It does what it needs to do.  Unless it doesn't - in which case, replace it with something better.
+// Obfuscated lazy port of an unfinished rlogin client I made quite some time
+// ago. It does what it needs to do.  Unless it doesn't - in which case,
+// replace it with something better.
 var RLogin=function(e){var n=this;const t=24,i=13,s=17,o=19,c=10;var r=[],p=[],u={connected:!1,cooked:!0,suspendInput:!1,suspendOutput:!1,watchForClientEscape:!0,clientHasEscaped:!1},d={rows:24,columns:80,pixelsX:640,pixelsY:480,clientEscape:"~"},f={DOT:n.disconnect,EOT:n.disconnect,SUB:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=u.suspendInput},EOM:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=!1}};this.__defineGetter__("connected",function(){return u.connected}),this.__defineSetter__("connected",function(e){"boolean"!=typeof e||e||n.disconnect()}),this.__defineGetter__("rows",function(){return d.rows}),this.__defineSetter__("rows",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'rows' setting "+e;d.rows=e}),this.__defineGetter__("columns",function(){return d.columns}),this.__defineSetter__("columns",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'columns' setting "+e;d.columns=e}),this.__defineGetter__("pixelsX",function(){return d.pixelsX}),this.__defineSetter__("pixelsX",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsX' setting "+e;d.pixelsX=e}),this.__defineGetter__("pixelsY",function(){return d.pixelsY}),this.__defineSetter__("pixelsY",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsY' setting "+e;d.pixelsY=e}),this.__defineGetter__("clientEscape",function(){return d.clientEscape}),this.__defineSetter__("clientEscape",function(e){if("string"!=typeof e||1!=e.length)throw"RLogin: Invalid 'clientEscape' setting "+e;d.clientEscape=e});var a=new Socket,l=function(){if(!(a.nread<1)){for(var e=[];a.nread>0;)e.push(a.recvBin(1));if(!u.connected)if(0==e[0]){if(u.connected=!0,!(e.length>1))return;e=e.slice(1)}else n.disconnect();u.suspendOutput||(r=r.concat(e))}};this.send=function(e){if(!u.connected)throw"RLogin.send: not connected.";if(u.suspendInput)throw"RLogin.send: input has been suspended.";"string"==typeof e&&(e=e.split("").map(function(e){return ascii(e)}));for(var n=[],r=0;r<e.length;r++)u.watchForClientEscape&&e[r]==d.clientEscape.charCodeAt(0)?(u.watchForClientEscape=!1,u.clientHasEscaped=!0):u.clientHasEscaped?(u.clientHasEscaped=!1,"undefined"!=typeof f[e[r]]&&f[e[r]]()):!u.cooked||e[r]!=s&&e[r]!=o?((r>0&&e[r-1]==i&&e[r]==c||e[r]==t)&&(u.watchForClientEscape=!0),n.push(e[r])):u.suspendOutput==(e[r]==o);p=p.concat(n)},this.receive=function(){return r.splice(0,r.length)},this.addClientEscape=function(e,n){if("string"!=typeof e&&"number"!=typeof e||"string"==typeof e&&e.length>1||"function"!=typeof n)throw"RLogin.addClientEscape: invalid arguments.";f[e.charCodeAt(0)]=n},this.connect=function(){if("number"!=typeof e.port||"string"!=typeof e.host)throw"RLogin: invalid host or port argument.";if("string"!=typeof e.clientUsername)throw"RLogin: invalid clientUsername argument.";if("string"!=typeof e.serverUsername)throw"RLogin: invalid serverUsername argument.";if("string"!=typeof e.terminalType)throw"RLogin: invalid terminalType argument.";if("number"!=typeof e.terminalSpeed)throw"RLogin: invalid terminalSpeed argument.";if(!a.connect(e.host,e.port))throw"RLogin: Unable to connect to server.";for(a.sendBin(0,1),a.send(e.clientUsername),a.sendBin(0,1),a.send(e.serverUsername),a.sendBin(0,1),a.send(e.terminalType+"/"+e.terminalSpeed),a.sendBin(0,1);a.is_connected&&a.nread<1;)mswait(5);l()},this.cycle=function(){if(l(),!(u.suspendInput||p.length<1))for(;p.length>0;)a.sendBin(p.shift(),1)},this.disconnect=function(){a.close(),u.connected=!1}};
 
 try {
@@ -29,8 +30,9 @@ try {
 		err('No cookie from WebSocket client.');
 	}
 
+	// Should probably search for the right cookie instead of assuming
 	var cookie = wss.headers['Cookie'].split('=');
-	if (cookie[0] !== "synchronet" || cookie.length < 2) {
+	if (cookie[0] !== 'synchronet' || cookie.length < 2) {
 		err('Invalid cookie from WebSocket client.');
 	}
 
-- 
GitLab


From b0790b7fb1cb682d8ce9a03b6c183013512e7fc7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Dec 2015 16:02:09 -0500
Subject: [PATCH 073/752] Simpler way to stop keyboard navigation annoyance
 when editing.

---
 web/root/js/forum.js | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index aa4858086f..13ea6ed3ef 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -118,15 +118,9 @@ function addNew(sub) {
 		}
 	);
 	window.location.hash = '#newmessage';
-	$('#newmessage-body').focus(
-		function () {
-			$(window).off('keydown');
-		}
-	);
-	$('#newmessage-body').blur(
-		function () {
-			window.location.hash = '';
-			threadNav();
+	$('#newmessage-body').keydown(
+		function (evt) {
+			evt.stopImmediatePropagation();
 		}
 	);
 }
@@ -150,12 +144,11 @@ function addReply(sub, id) {
 			);
 		}
 	);
-	$('#replytext-' + id).focus(
-		function () {
-			$(window).off('keydown');
+	$('#replytext-' + id).keydown(
+		function (evt) {
+			evt.stopImmediatePropagation();
 		}
 	);
-	$('#replytext-' + id).blur(threadNav);
 }
 
 // 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
-- 
GitLab


From 87743175d20d32c1b4a794fda40dfb2a5b79d5a7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Dec 2015 17:42:07 -0500
Subject: [PATCH 074/752] Added a 'max_messages' config key.  Only the first
 [max_messages] non-null, non-deleted messages in a sub-board will be loaded /
 sorted into threads / available via the web.  Default is 0 (no limit).
 Rewrote the getMessageThreads() function.  We were using the one from
 exec/load/msgutils.js.  This one should be slightly less memory-hungry.
 Sysops running into 'Out of memory' errors can tweak max_messages until the
 problem goes away.  Hopefully the new getMessageThreads() function will
 reduce the need to do this. The pre-sorted thread view makes pagination /
 partial loading of threads a bit tricky and would require an overhaul of the
 way all of this is done.  We could move toward that at some point, though.

---
 web/lib/forum.js              | 155 ++++++++++++++++++++++++++++++++--
 web/lib/init.js               |   6 +-
 web/root/pages/001-forum.ssjs |  72 +++++++++-------
 3 files changed, 195 insertions(+), 38 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 3566d296a1..9db65378b5 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,5 +1,4 @@
 load('sbbsdefs.js');
-load('msgutils.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
 
@@ -102,14 +101,14 @@ function getGroupUnreadCount(group) {
 
 function getUnreadInThread(sub, thread) {
     if (typeof thread === 'number') {
-        var threads = getMessageThreads(sub);
+        var threads = getMessageThreads(sub, settings.max_messages);
         if (typeof threads.thread[thread] === 'undefined') return 0;
         thread = threads.thread[thread];
     }
     var count = 0;
-    thread.messages.forEach(
-        function (header) {
-            if (header.number > msg_area.sub[sub].scan_ptr) count++;
+    Object.keys(thread.messages).forEach(
+        function (m) {
+            if (thread.messages[m].number > msg_area.sub[sub].scan_ptr) count++;
         }
     );
     return count;
@@ -577,3 +576,149 @@ function setScanCfg(sub, cfg) {
     return true;
 
 }
+
+var getMessageThreads = function(sub, max) {
+
+    var threads = { thread : {}, order : [] };
+    var subjects = {};
+
+    function addToThread(thread_id, header, subject) {
+        if (typeof subject !== 'undefined') subjects[subject] = thread_id;
+        threads.thread[thread_id].newest = header.when_written_time;
+        threads.thread[thread_id].messages[header.number] = {
+            number : header.number,
+            from : header.from,
+            from_net_addr : header.from_net_addr,
+            to : header.to,
+            when_written_time : header.when_written_time
+        };
+    }
+
+    function getSomeMessageHeaders(msgBase, start, count) {
+        if (start < 0 || start > msgBase.last_msg) return {};
+        if (start + count - 1 > msgBase.last_msg) {
+            count = msgBase.last_msg - start;
+        }
+        var headers = {};
+        for (var m = 0; m < count; m++) {
+            var header = msgBase.get_msg_header(start + m);
+            if (header === null || header.attr&MSG_DELETE) continue;
+            headers[header.number] = header;
+        }
+        return headers;
+    }
+
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return threads;
+    if ((typeof max === 'number' && max > 0) ||
+        typeof msgBase.get_all_msg_headers !== 'function'
+    ) {
+        var headers = getSomeMessageHeaders(msgBase, 0, max);
+    } else {
+        var headers = msgBase.get_all_msg_headers();
+    }
+    msgBase.close();
+
+    Object.keys(headers).forEach(
+
+        function(h) {
+
+            if (headers[h] === null || headers[h].attr&MSG_DELETE) {
+                delete headers[h];
+                return;
+            }
+
+            if (sub === 'mail' &&
+                headers[h].to !== user.alias &&
+                headers[h].to !== user.name &&
+                headers[h].to_ext !== user.number &&
+                headers[h].from !== user.alias &&
+                headers[h].from !== user.name &&
+                headers[h].from_ext !== user.number
+            ) {
+                delete headers[h];
+                return;
+            }
+
+            var subject = headers[h].subject.replace(/^(re:\s*)*/ig, '');
+
+            if (typeof subjects[subject] !== 'undefined') {
+
+                addToThread(subjects[subject], headers[h]);
+
+            } else if (headers[h].thread_id !== 0) {
+
+                if (typeof threads.thread[headers[h].thread_id]
+                    !== 'undefined'
+                ) {
+                    addToThread(headers[h].thread_id, headers[h], subject);
+                } else {
+                    threads.thread[headers[h].thread_id] = {
+                        id : headers[h].thread_id,
+                        newest : 0,
+                        subject : headers[h].subject,
+                        messages : {}
+                    };
+                    addToThread(headers[h].thread_id, headers[h], subject);
+                }
+
+            } else if (headers[h].thread_back !== 0) {
+
+                if (typeof threads.thread[headers[h].thread_back]
+                    !== 'undefined'
+                ) {
+                    addToThread(headers[h].thread_back, headers[h], subject);
+                } else {
+                    var threaded = false;
+                    for (var t in threads.thread) {
+                        if (typeof
+                            threads.thread[t].messages[headers[h].thread_back]
+                            !== 'undefined'
+                        ) {
+                            addToThread(t, headers[h], subject);
+                            threaded = true;
+                            break;
+                        }
+                    }
+                    if (!threaded) {
+                        threads.thread[headers[h].thread_back] = {
+                            id : headers[h].thread_back,
+                            newest : 0,
+                            subject : headers[h].subject,
+                            messages : {}
+                        };
+                        addToThread(
+                            headers[h].thread_back,
+                            headers[h],
+                            subject
+                        );
+                    }
+                }
+
+            } else {
+
+                threads.thread[headers[h].number] = {
+                    id : headers[h].number,
+                    newest : 0,
+                    subject : headers[h].subject,
+                    messages : {}
+                };
+                addToThread(headers[h].number, headers[h], subject);
+
+            }
+
+            delete headers[h];
+
+        }
+
+    );
+
+    threads.order = Object.keys(threads.thread).sort(
+        function (a, b) {
+            return threads.thread[b].newest - threads.thread[a].newest;
+        }
+    );
+
+    return threads;
+
+}
\ No newline at end of file
diff --git a/web/lib/init.js b/web/lib/init.js
index 3974e060e3..bc24315dee 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -53,4 +53,8 @@ if (typeof settings.xtrn_sections === 'string') {
 			return true;	
 		}
 	);
-}
\ No newline at end of file
+}
+
+if (typeof settings.max_messages !== 'number' || settings.max_messages < 0) {
+	settings.max_messages = 0;
+} 
\ No newline at end of file
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index acec717d18..8adaf70b5b 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -2,7 +2,6 @@
 if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 
 load('sbbsdefs.js');
-load('msgutils.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'forum.js');
 
@@ -29,7 +28,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	var firstUnread = '';
 
-	function writeMessage(header, index) {
+	function writeMessage(thread, keys, key, index) {
+
+		var header = thread.messages[key];
 
 		var body = msgBase.get_msg_body(header.number);
 		if (body === null) return;
@@ -42,7 +43,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		// Show subject if first message
 		if (index === 0) {
-			writeln('<h4><strong>' + header.subject + '</strong></h4>');
+			writeln('<h4><strong>' + thread.subject + '</strong></h4>');
 		}
 
 		// Header
@@ -69,21 +70,21 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		var prev = (
 			index === 0
-			? header.ec_thread.messages[0].number
-			: header.ec_thread.messages[index - 1].number
+			? thread.messages[thread.__first].number
+			: thread.messages[keys[index - 1]].number
 		);
 
 		var next = (
-			index === header.ec_thread.messages.length - 1
-			? header.ec_thread.messages[header.ec_thread.messages.length - 1].number
-			: header.ec_thread.messages[index + 1].number
+			key === thread.__last
+			? thread.messages[thread.__last].number
+			: thread.messages[keys[index + 1]].number
 		);
 
 		// Standard controls
 		writeln(
 			'<a class="btn btn-default icon" title="Jump to oldest message" ' +
 			'aria-label="Jump to oldest message" href="#' +
-			header.ec_thread.messages[0].number + '">' +
+			thread.messages[thread.__first].number + '">' +
 			'<span class="glyphicon glyphicon-fast-backward"></span></a>' +
 
 			'<a class="btn btn-default icon" title="Jump to previous message" '+
@@ -105,9 +106,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			'<a class="btn btn-default icon" ' +
 			'aria-label="Jump to newest message" ' +
 			'title="Jump to newest message" href="#' + 
-			header.ec_thread.messages[
-				header.ec_thread.messages.length - 1
-			].number + 
+			thread.messages[thread.__last].number + 
 			'"><span class="glyphicon glyphicon-fast-forward"></span></a>'
 		);
 
@@ -174,16 +173,24 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	try {
 		msgBase.open();
-		var messages = getMessageThreads(
-			http_request.query.sub[0]
+		var thread = getMessageThreads(
+			http_request.query.sub[0],
+			settings.max_messages
 		).thread[
 			http_request.query.thread[0]
-		].messages;
+		];
+		var keys = Object.keys(thread.messages);
+		thread.__first = keys[0];
+		thread.__last = keys[keys.length - 1];
 		writeln('<ul id="forum-list-container" class="list-group">');
-		messages.forEach(writeMessage);
+		keys.forEach(
+			function (key, index) {
+				writeMessage(thread, keys, key, index);
+			}
+		);
 		writeln('</ul>');
 		msgBase.close();
-		if (messages.length > 1 && firstUnread !== '') {
+		if (keys.length > 1 && firstUnread !== '') {
 			writeln(
 				'<script type="text/javascript">' +
 				'$("#jump-unread").attr("href", "#' + firstUnread + '");' +
@@ -192,11 +199,11 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			);
 		}
 		// Update scan pointer
-		if (messages[messages.length - 1].number >
+		if (thread.messages[thread.__last].number >
 			msg_area.sub[http_request.query.sub[0]].scan_ptr
 		) {
 			msg_area.sub[http_request.query.sub[0]].scan_ptr =
-			messages[messages.length - 1].number;
+			thread.messages[thread.__last].number;
 		}
 	} catch (err) {
 		log(err);
@@ -216,6 +223,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		} else {
 			var unread = 0;
 		}
+		var keys = Object.keys(thread.messages);
+		var first = keys[0];
+		var last = keys[keys.length - 1];
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" ' +
@@ -228,27 +238,22 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				'</a>',
 				http_request.query.page[0],
 				http_request.query.sub[0],
-				(	thread.messages[0].thread_id == 0
-					? thread.messages[0].number
-					: thread.messages[0].thread_id
-				),
-				thread.messages[0].subject,
-				thread.messages[0].from,
+				thread.id,
+				thread.subject,
+				thread.messages[first].from,
 				(new Date(
-						thread.messages[0].when_written_time * 1000
+						thread.messages[first].when_written_time * 1000
 					)
 				).toLocaleString(),
 				(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW
 					? ' scanned'
 					: ''
 				),
-				thread.messages[0].number,
+				thread.messages[first].number,
 				(unread == 0 ? "" : unread),
-				thread.messages[thread.messages.length - 1].from,
+				thread.messages[last].from,
 				(new Date(
-					thread.messages[
-						thread.messages.length - 1
-					].when_written_time * 1000
+					thread.messages[last].when_written_time * 1000
 				)).toLocaleString()
 			)
 		);
@@ -328,7 +333,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	}
 
 	try {
-		var threads = getMessageThreads(http_request.query.sub[0]);
+		var threads = getMessageThreads(
+			http_request.query.sub[0],
+			settings.max_messages
+		);
 		writeln('<div id="forum-list-container" class="list-group">');
 		threads.order.forEach(function(t){writeThread(threads.thread[t]);});
 		writeln('</div>');
-- 
GitLab


From 53ed21dfec59c9e00041a78b8a1472bb76ec8a92 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Dec 2015 17:53:11 -0500
Subject: [PATCH 075/752] Note re: max_messages setting

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README.md b/README.md
index 3ff47d8628..16b1601532 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,9 @@ I haven't actually tried these instructions on a clean Synchronet BBS installati
 	web_directory = ../web
 	; Path to a .ans file to use as the ftelnet splash screen
 	ftelnet_splash = ../text/synch.ans
+	; Only load this many messages from each sub (default: 0 for all)
+	; (If you get 'Out of memory' errors when viewing subs, tweak this setting)
+	max_messages = 0
 ```
 - Add the following section to your *ctrl/services.ini* file if it isn't there already:
 ```ini
-- 
GitLab


From e5f7140d5a32a1522f4304c40ad60994169cdf0d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Dec 2015 18:02:23 -0500
Subject: [PATCH 076/752] Log levels

---
 web/root/pages/001-forum.ssjs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 8adaf70b5b..4a49922be5 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -206,7 +206,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			thread.messages[thread.__last].number;
 		}
 	} catch (err) {
-		log(err);
+		log(LOG_WARNING, err);
 	}
 
 	writeln('<script type="text/javascript">threadNav();</script>');
@@ -341,7 +341,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		threads.order.forEach(function(t){writeThread(threads.thread[t]);});
 		writeln('</div>');
 	} catch (err) {
-		log(err);
+		log(LOG_WARNING, err);
 	}
 
 } else if (
@@ -414,7 +414,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			writeApiCall(subs);
 		}
 	} catch (err) {
-		log(err);
+		log(LOG_WARNING, err);
 	}
 
 } else {
@@ -477,7 +477,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			writeApiCall(groups);
 		}
 	} catch (err) {
-		log(err);
+		log(LOG_WARNING, err);
 	}
 
 }
\ No newline at end of file
-- 
GitLab


From 9e91a29bf1617d64385e7c39347dc49acb51190a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 18 Dec 2015 00:34:57 -0500
Subject: [PATCH 077/752] Loose comparison of index.to_ext and user.number in
 getMailUnreadCount().  Apparently to_ext is a string.

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 9db65378b5..2948edbdb4 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -121,7 +121,7 @@ function getMailUnreadCount() {
     for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
         var index = msgBase.get_msg_header(m);
         if (index === null) continue;
-        if (index.to_ext !== user.number) continue;
+        if (index.to_ext != user.number) continue;
         if (index.attr&MSG_READ) continue;
         if (index.attr&MSG_DELETE) continue;
         count++;
-- 
GitLab


From 592044a0c0e0e5a77540f986ad7ba2cf7bd4b685 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 19 Dec 2015 03:23:23 -0500
Subject: [PATCH 078/752] Fix sending of new/reply private internet email.

---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 2948edbdb4..13e7908d2d 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -295,9 +295,8 @@ function postMail(header, body) {
     }
     var ret = false;
     if (user.number < 1 || user.alias === settings.guest) return ret;
-    var na = netaddr_type(header.to);
+    var na = netaddr_type(header.to_net_addr);
     header.to_net_type = na;
-    if (na > 0) header.to_net_addr = header.to;
     if (na === NET_NONE) {
         var un = system.matchuser(header.to);
         if (un === 0) return false; // Should actually inform about this
@@ -330,6 +329,7 @@ function postNew(sub, to, subject, body) {
         subject : subject
     };
     if (sub === 'mail') {
+        header.to_net_addr = header.to;
         return postMail(header, body);
     } else {
         return postMessage(sub, header, body);
-- 
GitLab


From 86d3876202014c309a4d884d9dc1d56b3fe7aef8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Dec 2015 10:10:19 -0500
Subject: [PATCH 079/752] Check that the user has read access to a sub when
 loading thread-list or message-level view.

---
 web/root/pages/001-forum.ssjs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 4a49922be5..aabbea8353 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -20,7 +20,8 @@ if (typeof http_request.query.notice !== 'undefined') {
 
 if (typeof http_request.query.sub !== 'undefined' &&
 	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined' &&
-	typeof http_request.query.thread !== 'undefined'
+	typeof http_request.query.thread !== 'undefined' &&
+	msg_area.sub[http_request.query.sub[0]].can_read
 ) {
 
 	// Thread view
@@ -213,7 +214,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 } else if (
 	typeof http_request.query.sub !== 'undefined' &&
-	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined'
+	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined' &&
+	msg_area.sub[http_request.query.sub[0]].can_read
 ) {
 
 	// Thread list
-- 
GitLab


From 61d8a9f237576b9f3db8d7079eb504315fc9fca1 Mon Sep 17 00:00:00 2001
From: "Michael J. Ryan" <tracker1@gmail.com>
Date: Sat, 26 Dec 2015 05:48:21 -0700
Subject: [PATCH 080/752] Update auth.js

remove evaluation context in favor of ife.
---
 web/lib/auth.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 8e962386bf..301306e179 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -152,9 +152,9 @@ if (typeof http_request.query.username !== 'undefined' &&
 // If they have a cookie
 } else if(
 	typeof http_request.cookie.synchronet != 'undefined' &&
-	http_request.cookie.synchronet.some(
-		Function('e', 'return(e.search(/^\\d+,\\w+$/) != -1)')
-	)
+	http_request.cookie.synchronet.some(function(e){
+		return(e.search(/^\\d+,\\w+$/) != -1);
+	})
 ) {
 	// Verify & update their session, or log them out if requested
 	if (typeof http_request.query.logout === 'undefined') {
@@ -176,4 +176,4 @@ if (user.number === 0) {
 		// Otherwise just kill the script, for security's sake
 		exit();
 	}
-}
\ No newline at end of file
+}
-- 
GitLab


From 3326122b6b792363d5f1e6bdfff4288d65716575 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 27 Dec 2015 01:42:30 -0500
Subject: [PATCH 081/752] Test header for 'from_net_addr' rather than
 'from_net_type' when displaying 'From' field in thread-level view.

---
 web/root/pages/001-forum.ssjs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index aabbea8353..5893fd8437 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -58,7 +58,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		}			
 		writeln(
 			'From <strong>' + header.from + "</strong>" +
-			(header.from_net_type == 0 ? '' : ('@' +  header.from_net_addr)) +
+			(	typeof header.from_net_addr === 'undefined'
+				? ''
+				: ('@' +  header.from_net_addr)
+			) +
 			' to <strong>' + header.to + '</strong> on ' +
 			(new Date(header.when_written_time * 1000)).toLocaleString()
 		);
-- 
GitLab


From 6dd1a2328f58ca0e937c81c433fe26acb535600f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 27 Dec 2015 02:09:53 -0500
Subject: [PATCH 082/752] Fix error in previous commit (broke authentication.)
 The regex isn't inside of a string any longer, so we don't need to escape the
 escapes.

---
 web/lib/auth.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 301306e179..ae18212050 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -152,9 +152,11 @@ if (typeof http_request.query.username !== 'undefined' &&
 // If they have a cookie
 } else if(
 	typeof http_request.cookie.synchronet != 'undefined' &&
-	http_request.cookie.synchronet.some(function(e){
-		return(e.search(/^\\d+,\\w+$/) != -1);
-	})
+	http_request.cookie.synchronet.some(
+		function (e) {
+		 	return(e.search(/^\d+,\w+$/) != -1);
+		}
+	)
 ) {
 	// Verify & update their session, or log them out if requested
 	if (typeof http_request.query.logout === 'undefined') {
-- 
GitLab


From 8ae955d79202e0a9f8cb89eb9278ff0440f3021b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 27 Dec 2015 15:18:16 -0500
Subject: [PATCH 083/752] When posting a reply, only set to_net_addr if sub is
 'mail'. Set thread_id for replies, to parent's thread_id if available, or
 parent message number otherwise.

---
 web/lib/forum.js | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 13e7908d2d..edcfb1e559 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -353,12 +353,19 @@ function postReply(sub, body, pid) {
         if (pHeader === null) return ret;
         var header = {
             'to' : pHeader.from,
-            'to_net_addr' : pHeader.from_net_addr,
             'from' : user.alias,
             'subject' : pHeader.subject,
+            'thread_id' : (
+                typeof pHeader.thread_id === 'undefined'
+                ? pHeader.number
+                : pHeader.thread_id
+            ),
             'thread_back' : pHeader.number
         };
         if (sub === 'mail') {
+            if (typeof pHeader.from_net_addr !== 'undefined') {
+                header.to_net_addr = pHeader.from_net_addr;
+            }
             ret = postMail(header, body);
         } else {
             ret = postMessage(sub, header, body);
-- 
GitLab


From f9e65fc96887478bfcb00a90f2eb25800a5e7fc6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 31 Dec 2015 02:52:09 -0500
Subject: [PATCH 084/752] De-obfuscate crappy RLogin client.  Meh.

---
 mods/websocket-rlogin-service.js | 346 ++++++++++++++++++++++++++++++-
 1 file changed, 340 insertions(+), 6 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 9ba685899b..7d653ec550 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -17,10 +17,342 @@ function getSession(un) {
 	return session;
 }
 
-// Obfuscated lazy port of an unfinished rlogin client I made quite some time
-// ago. It does what it needs to do.  Unless it doesn't - in which case,
-// replace it with something better.
-var RLogin=function(e){var n=this;const t=24,i=13,s=17,o=19,c=10;var r=[],p=[],u={connected:!1,cooked:!0,suspendInput:!1,suspendOutput:!1,watchForClientEscape:!0,clientHasEscaped:!1},d={rows:24,columns:80,pixelsX:640,pixelsY:480,clientEscape:"~"},f={DOT:n.disconnect,EOT:n.disconnect,SUB:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=u.suspendInput},EOM:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=!1}};this.__defineGetter__("connected",function(){return u.connected}),this.__defineSetter__("connected",function(e){"boolean"!=typeof e||e||n.disconnect()}),this.__defineGetter__("rows",function(){return d.rows}),this.__defineSetter__("rows",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'rows' setting "+e;d.rows=e}),this.__defineGetter__("columns",function(){return d.columns}),this.__defineSetter__("columns",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'columns' setting "+e;d.columns=e}),this.__defineGetter__("pixelsX",function(){return d.pixelsX}),this.__defineSetter__("pixelsX",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsX' setting "+e;d.pixelsX=e}),this.__defineGetter__("pixelsY",function(){return d.pixelsY}),this.__defineSetter__("pixelsY",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsY' setting "+e;d.pixelsY=e}),this.__defineGetter__("clientEscape",function(){return d.clientEscape}),this.__defineSetter__("clientEscape",function(e){if("string"!=typeof e||1!=e.length)throw"RLogin: Invalid 'clientEscape' setting "+e;d.clientEscape=e});var a=new Socket,l=function(){if(!(a.nread<1)){for(var e=[];a.nread>0;)e.push(a.recvBin(1));if(!u.connected)if(0==e[0]){if(u.connected=!0,!(e.length>1))return;e=e.slice(1)}else n.disconnect();u.suspendOutput||(r=r.concat(e))}};this.send=function(e){if(!u.connected)throw"RLogin.send: not connected.";if(u.suspendInput)throw"RLogin.send: input has been suspended.";"string"==typeof e&&(e=e.split("").map(function(e){return ascii(e)}));for(var n=[],r=0;r<e.length;r++)u.watchForClientEscape&&e[r]==d.clientEscape.charCodeAt(0)?(u.watchForClientEscape=!1,u.clientHasEscaped=!0):u.clientHasEscaped?(u.clientHasEscaped=!1,"undefined"!=typeof f[e[r]]&&f[e[r]]()):!u.cooked||e[r]!=s&&e[r]!=o?((r>0&&e[r-1]==i&&e[r]==c||e[r]==t)&&(u.watchForClientEscape=!0),n.push(e[r])):u.suspendOutput==(e[r]==o);p=p.concat(n)},this.receive=function(){return r.splice(0,r.length)},this.addClientEscape=function(e,n){if("string"!=typeof e&&"number"!=typeof e||"string"==typeof e&&e.length>1||"function"!=typeof n)throw"RLogin.addClientEscape: invalid arguments.";f[e.charCodeAt(0)]=n},this.connect=function(){if("number"!=typeof e.port||"string"!=typeof e.host)throw"RLogin: invalid host or port argument.";if("string"!=typeof e.clientUsername)throw"RLogin: invalid clientUsername argument.";if("string"!=typeof e.serverUsername)throw"RLogin: invalid serverUsername argument.";if("string"!=typeof e.terminalType)throw"RLogin: invalid terminalType argument.";if("number"!=typeof e.terminalSpeed)throw"RLogin: invalid terminalSpeed argument.";if(!a.connect(e.host,e.port))throw"RLogin: Unable to connect to server.";for(a.sendBin(0,1),a.send(e.clientUsername),a.sendBin(0,1),a.send(e.serverUsername),a.sendBin(0,1),a.send(e.terminalType+"/"+e.terminalSpeed),a.sendBin(0,1);a.is_connected&&a.nread<1;)mswait(5);l()},this.cycle=function(){if(l(),!(u.suspendInput||p.length<1))for(;p.length>0;)a.sendBin(p.shift(),1)},this.disconnect=function(){a.close(),u.connected=!1}};
+var RLoginClient = function(options) {
+
+	var self = this;
+
+	const	CAN = 0x18,
+			CR = 0x0D,
+			DC1 = 0x11,
+			DC3 = 0x13,
+			DOT = 0x2E,
+			EOM = 0x19,
+			EOT = 0x04,
+			LF = 0x0A,
+			SUB = 0x1A,
+			DISCARD = 0x02,
+			RAW = 0x10,
+			COOKED = 0x20,
+			WINDOW = 0x80;
+
+	var serverBuffer = []; // From server
+	var clientBuffer = []; // From client
+
+	var state = {
+		connected : false,
+		cooked : true,
+		suspendInput : false,
+		suspendOutput : false,
+		watchForClientEscape : true,
+		clientHasEscaped : false
+	};
+
+	var properties = {
+		rows : 24,
+		columns : 80,
+		pixelsX : 640,
+		pixelsY : 480,
+		clientEscape : '~'
+	};
+
+	// As suggested by RFC1282
+	var clientEscapes = {
+		DOT : self.disconnect,
+		EOT : self.disconnect,
+		SUB : function() {
+			state.suspendInput = (state.suspendInput) ? false : true;
+			state.suspendOutput = state.suspendInput;
+		},
+		EOM : function() {
+			state.suspendInput = (state.suspendInput) ? false : true;
+			state.suspendOutput = false;
+		}
+	};
+
+	this.__defineGetter__('connected', function () { return state.connected; });
+
+	this.__defineSetter__(
+		'connected',
+		function (value) {
+			if (typeof value === 'boolean' && !value) self.disconnect();
+		}
+	);
+
+	this.__defineGetter__('rows', function () { return properties.rows; });
+
+	this.__defineSetter__(
+		'rows',
+		function(value) {
+			if (typeof value === 'number' && value > 0) {
+				properties.rows = value;
+			} else {
+				throw 'RLogin: Invalid \'rows\' setting ' + value;
+			}
+		}
+	);
+
+	this.__defineGetter__(
+		'columns',
+		function () { return properties.columns; }
+	);
+
+	this.__defineSetter__(
+		'columns',
+		function (value) {
+			if (typeof value === 'number' && value > 0) {
+				properties.columns = value;
+			} else {
+				throw 'RLogin: Invalid \'columns\' setting ' + value;
+			}
+		}
+	);
+
+	this.__defineGetter__(
+		'pixelsX',
+		function () { return properties.pixelsX; }
+	);
+
+	this.__defineSetter__(
+		'pixelsX',
+		function (value) {
+			if (typeof value === 'number' && value > 0) {
+				properties.pixelsX = value;
+			} else {
+				throw 'RLogin: Invalid \'pixelsX\' setting ' + value;
+			}
+		}
+	);
+
+	this.__defineGetter__(
+		'pixelsY',
+		function () { return properties.pixelsY; }
+	);
+
+	this.__defineSetter__(
+		'pixelsY',
+		function (value) {
+			if (typeof value === 'number' && value > 0) {
+				properties.pixelsY = value;
+			} else {
+				throw 'RLogin: Invalid \'pixelsY\' setting ' + value;
+			}
+		}
+	);
+
+	this.__defineGetter__(
+		'clientEscape',
+		function() { return properties.clientEscape; }
+	);
+
+	this.__defineSetter__(
+		'clientEscape',
+		function (value) {
+			if (typeof value === 'string' && value.length === 1) {
+				properties.clientEscape = value;
+			} else {
+				throw 'RLogin: Invalid \'clientEscape\' setting ' + value;
+			}
+		}
+	);
+
+	var handle = new Socket();
+
+	function getServerData() {
+
+		if (handle.nread < 1) return;
+
+		var data = [];
+		while (handle.nread > 0) {
+			data.push(handle.recvBin(1));
+		}
+
+		if (!state.connected) {
+			if (data[0] === 0) {
+				state.connected = true;
+				if (data.length > 1) {
+					data = data.slice(1);
+				} else {
+					return;
+				}
+			} else {
+				self.disconnect();
+			}
+		}
+
+		// If I could tell if the TCP urgent-data pointer had been set,
+		// I would uncomment (and complete) this block.  We'll settle
+		// for a partial implementation for the time being.
+		// We would need something to tell us if urgent data was sent,
+		// eg. var lookingForControlCode = urgentDataPointerIsSet();
+		/*
+		var temp = [];
+		for (var d = 0; d < data.length; d++) {
+			if (!lookingForControlCode) {
+				temp.push(data[d]);
+				continue;
+			}
+			switch (data[d]) {
+				case DISCARD:
+					temp = [];
+					// We found our control code
+					lookingForControlCode = false;
+					break;
+				case RAW:
+					state.cooked = false;
+					lookingForControlCode = false;
+					break;
+				case COOKED:
+					state.cooked = true;
+					lookingForControlCode = false;
+					break;
+				case WINDOW:
+					self.sendWCCS();
+					lookingForControlCode = false;
+					break;
+				default:
+					temp.push(data[d]);
+					break;
+			}
+		}
+		if (!state.suspendOutput) self.emit('data', new Buffer(temp));
+		*/
+		if (!state.suspendOutput) serverBuffer = serverBuffer.concat(data);
+	}
+
+	// Send a Window Change Control Sequence
+	// this.sendWCCS = function() {
+	// 	var magicCookie = [0xFF, 0xFF, 0x73, 0x73];
+	// 	var rcxy = new Buffer(8);
+	// 	rcxy.writeUInt16LE(properties.rows, 0);
+	// 	rcxy.writeUInt16LE(properties.columns, 2);
+	// 	rcxy.writeUInt16LE(properties.pixelsX, 4);
+	// 	rcxy.writeUInt16LE(properties.pixelsY, 6);
+	// 	if(state.connected)
+	// 		handle.write(Buffer.concat([magicCookie, rcxy]));
+	// }
+
+	// Send 'data' (String or Buffer) to the rlogin server
+	this.send = function (data) {
+
+		if (!state.connected) throw 'RLogin.send: not connected.';
+		if (state.suspendInput) throw 'RLogin.send: input has been suspended.';
+		
+		if (typeof data === 'string') {
+			data = data.split('').map(function (d) { return ascii(d); });
+		}
+
+		var temp = [];
+		for (var d = 0; d < data.length; d++) {
+			if (state.watchForClientEscape &&
+				data[d] == properties.clientEscape.charCodeAt(0)
+			) {
+				state.watchForClientEscape = false;
+				state.clientHasEscaped = true;
+				continue;
+			}
+			if (state.clientHasEscaped) {
+				state.clientHasEscaped = false;
+				if (typeof clientEscapes[data[d]] !== 'undefined') {
+					clientEscapes[data[d]]();
+				}
+				continue;
+			}
+			if (state.cooked && (data[d] === DC1 || data[d] === DC3)) {
+				state.suspendOutput = (data[d] === DC3);
+				continue;
+			}
+			if ((d > 0 && data[d - 1] === CR && data[d] === LF) ||
+				data[d] == CAN
+			) {
+				state.watchForClientEscape = true;
+			}
+			temp.push(data[d]);
+		}
+		clientBuffer = clientBuffer.concat(temp);
+
+	}
+
+	this.receive = function () {
+		return serverBuffer.splice(0, serverBuffer.length);
+	}
+
+	/*	If 'ch' is found in client input immediately after the
+		'this.clientEscape' character when:
+			- this is the first input after connection establishment or
+			- these are the first characters on a new line or
+			- these are the first characters after a line-cancel character
+		then the function 'callback' will be called.  Use this to allow
+		client input to trigger a particular action.	*/
+	this.addClientEscape = function (ch, callback) {
+		if(	(typeof ch !== 'string' && typeof ch !== 'number') ||
+			(typeof ch === 'string' && ch.length > 1) ||
+			typeof callback !== 'function'
+		) {
+			throw 'RLogin.addClientEscape: invalid arguments.';
+		}
+		clientEscapes[ch.charCodeAt(0)] = callback;
+	}
+
+	this.connect = function () {
+		
+		if (typeof options.port !== 'number' ||
+			typeof options.host != 'string'
+		) {
+			throw 'RLogin: invalid host or port argument.';
+		}
+		
+		if (typeof options.clientUsername !== 'string') {
+			throw 'RLogin: invalid clientUsername argument.';
+		}
+		
+		if (typeof options.serverUsername !== 'string') {
+			throw 'RLogin: invalid serverUsername argument.';
+		}
+		
+		if (typeof options.terminalType !== 'string') {
+			throw 'RLogin: invalid terminalType argument.';
+		}
+
+		if (typeof options.terminalSpeed !== 'number') {
+			throw 'RLogin: invalid terminalSpeed argument.';
+		}
+
+		if (handle.connect(options.host, options.port)) {
+			handle.sendBin(0, 1);
+			handle.send(options.clientUsername);
+			handle.sendBin(0, 1);
+			handle.send(options.serverUsername);
+			handle.sendBin(0, 1);
+			handle.send(options.terminalType + '/' + options.terminalSpeed);
+			handle.sendBin(0, 1);
+			while (handle.is_connected && handle.nread < 1) {
+				mswait(5);
+			}
+			getServerData();
+		} else {
+			throw 'RLogin: Unable to connect to server.';
+		}
+
+	}
+
+	this.cycle = function () {
+
+		getServerData();
+
+		if (state.suspendInput || clientBuffer.length < 1) return;
+
+		while (clientBuffer.length > 0) {
+			handle.sendBin(clientBuffer.shift(), 1);
+		}
+
+	}
+
+	this.disconnect = function () {
+		handle.close();
+		state.connected = false;
+	}
+
+}
 
 try {
 
@@ -65,7 +397,7 @@ try {
 	var ini = f.iniGetObject('BBS');
 	f.close();
 
-	rlogin = new RLogin(
+	rlogin = new RLoginClient(
 		{	host : system.inet_addr,
 			port : ini.RLoginPort,
 			clientUsername : usr.security.password,
@@ -90,11 +422,13 @@ try {
 			rlogin.send(data);
 		}
 
+		mswait(5);
+
 	}
 
 } catch (err) {
 
-	log(err);
+	log(LOG_ERR, err);
 
 } finally {
 	rlogin.disconnect();
-- 
GitLab


From f9e0d6c26b9a64649eff2784451d9f33d0e95df5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 31 Dec 2015 02:53:00 -0500
Subject: [PATCH 085/752] Initial commit.

---
 mods/websocket-telnet-service.js | 261 +++++++++++++++++++++++++++++++
 1 file changed, 261 insertions(+)
 create mode 100644 mods/websocket-telnet-service.js

diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
new file mode 100644
index 0000000000..8764855ffc
--- /dev/null
+++ b/mods/websocket-telnet-service.js
@@ -0,0 +1,261 @@
+load('sbbsdefs.js');
+load('websocket-proxy.js');
+
+function log_err(msg) {
+	log(LOG_DEBUG, msg);
+	client.socket.close();
+	exit();
+}
+
+var TelnetClient = function(host, port) {
+
+    var MAX_BUFFER = 32767;
+
+    var TELNET_DATA = 0;
+    var TELNET_IAC = 1;
+    var TELNET_SUBNEGOTIATE = 2;
+    var TELNET_SUBNEGOTIATE_IAC = 3;
+    var TELNET_WILL = 4;
+    var TELNET_WONT = 5;
+    var TELNET_DO = 6;
+    var TELNET_DONT = 7;
+
+    var TELNET_CMD_TTYLOC = 28;
+
+    var state = TELNET_DATA;
+
+    var buffers = {
+        rx : [], // From server
+        tx : [] // To server
+    };
+
+    var socket = new Socket();
+    socket.connect(host, port);
+
+    this.__defineGetter__(
+        'connected',
+        function () {
+            return socket.is_connected;
+        }
+    );
+
+    this.__defineSetter__(
+        'connected',
+        function (bool) {
+            if (!bool && socket.is_connected) {
+                socket.close();
+            } else if (bool && !socket.is_connected) {
+                socket.connect(host, port);
+            }
+        }
+    );
+
+    this.__defineGetter__(
+        'data_waiting',
+        function () {
+            return (buffers.rx.length > 0);
+        }
+    );
+
+    function receive() {
+
+        var rx = [];
+
+        while (socket.data_waiting && rx.length < MAX_BUFFER) {
+            var nr = (socket.nread >= 4 ? 4 : (socket.nread >= 2 ? 2 : 1));
+            var bin = socket.recvBin(nr);
+            if (nr === 4) {
+                rx.push((bin&(255<<24))>>>24);
+                rx.push((bin&(255<<16))>>>16);
+            }
+            if (nr >= 2) rx.push((bin&(255<<8))>>>8);
+            rx.push(bin&255);
+        }
+
+        while (rx.length > 0) {
+            var b = rx.shift();
+            switch (state) {
+                case TELNET_DATA:
+                    if (b == 0xFF) {
+                        state = TELNET_IAC;
+                    } else {
+                        buffers.rx.push(b);
+                    }
+                    break;
+                case TELNET_IAC:
+                    switch (b) {
+                        case 0xF1: // NOP: No operation.
+                        case 0xF2: // Data Mark: The data stream portion of a Synch. This should always be accompanied by a TCP Urgent notification.
+                        case 0xF3: // Break: NVT character BRK.
+                        case 0xF4: // Interrupt Process: The function IP.
+                        case 0xF5: // Abort output: The function AO.
+                        case 0xF6: // Are You There: The function AYT.
+                        case 0xF7: // Erase character: The function EC.
+                        case 0xF8: // Erase Line: The function EL.
+                        case 0xF9: // Go ahead: The GA signal
+                            // Ignore these single byte commands
+                            state = TELNET_DATA;
+                            break;
+                        case 0xFA: // Subnegotiation
+                            state = TELNET_SUBNEGOTIATE;
+                            break;
+                        case 0xFB: // Will
+                            state = TELNET_WILL;
+                            break;
+                        case 0xFC: // Wont
+                            state = TELNET_WONT;
+                            break;
+                        case 0xFD: // Do
+                            state = TELNET_DO;
+                            break;
+                        case 0xFE: // Dont
+                            state = TELNET_DONT;
+                            break;
+                        case 0xFF:
+                            buffers.rx.push(0xFF);
+                            state = TELNET_DATA;
+                            break;
+                    }
+                    break;
+                case TELNET_SUBNEGOTIATE:
+                    if (b == 0xFF) state = TELNET_SUBNEGOTIATE_IAC;
+                    break;
+                case TELNET_SUBNEGOTIATE_IAC:
+                    if (b == 0xFF) {
+                        state = TELNET_SUBNEGOTIATE;
+                    } else if (b == 0xF0) {
+                        state = TELNET_DATA;
+                    } else {
+                        // Unexpected
+                        state = TELNET_DATA;
+                    }
+                    break;
+                case TELNET_DO:
+                    switch (b) {
+                        // This will bork with IPV6
+                        case TELNET_CMD_TTYLOC:
+                            socket.sendBin(255, 1);
+                            socket.sendBin(250, 1);
+                            socket.sendBin(28, 1);
+                            socket.sendBin(0, 1);
+                            client.ip_address.split('.').forEach(
+                                function (e) {
+                                    e = parseInt(e);
+                                    socket.sendBin(e, 1);
+                                    if (e === 255) socket.sendBin(e, 1);
+                                }
+                            );
+                            client.ip_address.split('.').forEach(
+                                function (e) {
+                                    e = parseInt(e);
+                                    socket.sendBin(e, 1);
+                                    if (e === 255) socket.sendBin(e, 1);
+                                }
+                            );
+                            socket.sendBin(255, 1);
+                            socket.sendBin(240, 1);
+                            break;
+                        default:
+                            break;
+                    }
+                    state = TELNET_DATA;
+                    break;
+                case TELNET_WILL:
+                case TELNET_WONT:
+                case TELNET_DONT:
+                    state = TELNET_DATA;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+    }
+
+    function transmit() {
+
+        if (!socket.is_connected || buffers.tx.length < 1) return;
+
+        while (buffers.tx.length >= 4) {
+            var chunk = (buffers.tx.shift()<<24);
+            chunk |= (buffers.tx.shift()<<16);
+            chunk |= (buffers.tx.shift()<<8);
+            chunk |= buffers.tx.shift();
+            socket.sendBin(chunk, 4);
+        }
+
+        if (buffers.tx.length >= 2) {
+            var chunk = (buffers.tx.shift()<<8);
+            chunk |= buffers.tx.shift();
+            socket.sendBin(chunk, 2);
+        }
+
+        if (buffers.tx.length > 0) {
+            socket.sendBin(buffers.tx.shift(), 1);
+        }
+
+    }
+
+    this.receive = function () {
+        return buffers.rx.splice(0, buffers.rx.length);
+    }
+
+    this.send = function (arr) {
+
+        if (typeof arr === 'string') {
+            var arr = arr.map(
+                function (e) {
+                    return ascii(e);
+                }
+            );
+        }
+
+        buffers.tx = buffers.tx.concat(arr);
+
+    }
+
+    this.cycle = function () {
+        receive();
+        transmit();
+    }
+
+}
+
+try {
+
+    var f = new File(file_cfgname(system.ctrl_dir, 'sbbs.ini'));
+    if (!f.open('r')) log_err('Unable to open sbbs.ini.');
+    var ini = f.iniGetObject('BBS');
+    f.close();
+
+    var wss = new WebSocketProxy(client);
+    var telnet = new TelnetClient(system.inet_addr, ini.TelnetPort);
+ 
+	while (client.socket.is_connected && telnet.connected) {
+
+    	wss.cycle();
+        telnet.cycle();
+
+        if (telnet.data_waiting) {
+            var data = telnet.receive();
+            wss.send(data);
+        }
+
+		while (wss.data_waiting) {
+			var data = wss.receiveArray();
+            telnet.send(data);
+		}
+
+        mswait(5);
+
+	}
+
+} catch (err) {
+
+	log(LOG_ERR, 'Caught: ' + err);
+
+} finally {
+
+	client.socket.close();
+
+}
\ No newline at end of file
-- 
GitLab


From 352acc3d6a94b26e916fd5cbafc1b1280350504c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 31 Dec 2015 02:57:25 -0500
Subject: [PATCH 086/752] er

---
 mods/websocket-rlogin-service.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 7d653ec550..0672ea3362 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -426,9 +426,9 @@ try {
 
 	}
 
-} catch (err) {
+} catch (er) {
 
-	log(LOG_ERR, err);
+	log(LOG_ERR, er);
 
 } finally {
 	rlogin.disconnect();
-- 
GitLab


From 032fba7fb9277c5dc2b1b516d2ebe2a929f3b27a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 1 Jan 2016 04:00:13 -0500
Subject: [PATCH 087/752] Add ip_address to session store, reset during session
 validation.

---
 web/lib/auth.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index ae18212050..afe8b750e0 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -73,6 +73,7 @@ function validateSession(cookies) {
 
 		authenticate(usr.alias, usr.security.password);
 		setCookie(usr, session.key);
+		setSessionValue(usr.number, 'ip_address', client.ip_address);
 		break;
 
 	}
@@ -151,7 +152,7 @@ if (typeof http_request.query.username !== 'undefined' &&
 	if (usr instanceof User) setCookie(usr, randomString(512));
 // If they have a cookie
 } else if(
-	typeof http_request.cookie.synchronet != 'undefined' &&
+	typeof http_request.cookie.synchronet !== 'undefined' &&
 	http_request.cookie.synchronet.some(
 		function (e) {
 		 	return(e.search(/^\d+,\w+$/) != -1);
-- 
GitLab


From 53c555854234391307576bebc004d2692086c515 Mon Sep 17 00:00:00 2001
From: Josh Renaud <josh@joshrenaud.com>
Date: Sat, 2 Jan 2016 16:37:45 -0600
Subject: [PATCH 088/752] Most recent user isn't showing in list

change comparison so that my newest user will show up in the userlist.
---
 web/root/pages/003_userlist.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/003_userlist.xjs b/web/root/pages/003_userlist.xjs
index 63358a3bfc..ddb517f178 100644
--- a/web/root/pages/003_userlist.xjs
+++ b/web/root/pages/003_userlist.xjs
@@ -141,7 +141,7 @@
 
 	function loadUsers(offset, pageSize) {
 		var users = [];
-		for (var u = 1; u < system.lastuser; u++) {
+		for (var u = 1; u <= system.lastuser; u++) {
 			var usr = new User(u);
 			if (usr.settings&USER_DELETED ||
 				usr.compare_ars('REST Q') ||
-- 
GitLab


From 30c95b9f5d780944c0240523c5dc92272975bb1a Mon Sep 17 00:00:00 2001
From: Josh Renaud <josh@joshrenaud.com>
Date: Sat, 2 Jan 2016 16:48:11 -0600
Subject: [PATCH 089/752] Add a little bit of flexibility for the date
 formatting

Added the js-date-format.js library to allow a bit of flexibility in the
way dates are displayed in the userlist.
---
 mods/js-date-format.js          | 290 ++++++++++++++++++++++++++++++++
 web/root/pages/003_userlist.xjs |  28 ++-
 2 files changed, 311 insertions(+), 7 deletions(-)
 create mode 100644 mods/js-date-format.js

diff --git a/mods/js-date-format.js b/mods/js-date-format.js
new file mode 100644
index 0000000000..d21b35a596
--- /dev/null
+++ b/mods/js-date-format.js
@@ -0,0 +1,290 @@
+/**
+* js-date-format (js-date-format.js)
+* v1.0
+* (c) Tony Brix (tonybrix.info) - 2014.
+* https://github.com/UziTech/js-date-format
+*
+* MIT License
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+* associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+Date.prototype.setLocale = function (lang) {
+	if (lang && lang in Date.locales) {
+		this.locale = lang;
+	}
+};
+
+Date.prototype.getLocale = function () {
+	return this.locale || "en";
+};
+
+Date.prototype.getMonthName = function (lang) {
+	var locale = "en";
+	if (lang && lang in Date.locales) {
+		locale = lang;
+	} else if (this.locale && this.locale in Date.locales) {
+		locale = this.locale;
+	}
+	return Date.locales[locale].month_names[this.getMonth()];
+};
+
+Date.prototype.getMonthNameShort = function (lang) {
+	var locale = "en";
+	if (lang && lang in Date.locales) {
+		locale = lang;
+	} else if (this.locale && this.locale in Date.locales) {
+		locale = this.locale;
+	}
+	return Date.locales[locale].month_names_short[this.getMonth()];
+};
+
+Date.prototype.getDayName = function (lang) {
+	var locale = "en";
+	if (lang && lang in Date.locales) {
+		locale = lang;
+	} else if (this.locale && this.locale in Date.locales) {
+		locale = this.locale;
+	}
+	return Date.locales[locale].day_names[this.getDay()];
+};
+
+Date.prototype.getDayNameShort = function (lang) {
+	var locale = "en";
+	if (lang && lang in Date.locales) {
+		locale = lang;
+	} else if (this.locale && this.locale in Date.locales) {
+		locale = this.locale;
+	}
+	return Date.locales[locale].day_names_short[this.getDay()];
+};
+
+Date.prototype.getDateSuffix = function (lang) {
+	var locale = "en";
+	if (lang && lang in Date.locales) {
+		locale = lang;
+	} else if (this.locale && this.locale in Date.locales) {
+		locale = this.locale;
+	}
+	return Date.locales[locale].date_suffix(this.getDate());
+};
+
+Date.prototype.getMeridiem = function (isLower, lang) {
+	var locale = "en";
+	if (lang && lang in Date.locales) {
+		locale = lang;
+	} else if (this.locale && this.locale in Date.locales) {
+		locale = this.locale;
+	}
+	return Date.locales[locale].meridiem(this.getHours(), this.getMinutes(), isLower);
+};
+
+Date.prototype.getLastDate = function () {
+	return (new Date(this.getFullYear(), this.getMonth() + 1, 0)).getDate();
+};
+
+/* languages from http://momentjs.com */
+Date.locales = {
+	"en": {
+		month_names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+		month_names_short: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+		day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+		day_names_short: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+		date_suffix: function (date) {
+			var day10 = ~~ (date % 100 / 10);
+			var day1 = date % 10;
+			if (day10 === 1) {
+				return "th";
+			} else if (day1 === 1) {
+				return "st";
+			} else if (day1 === 2) {
+				return "nd";
+			} else if (day1 === 3) {
+				return "rd";
+			} else {
+				return "th";
+			}
+		},
+		meridiem : function (hour, minute, isLower) {
+			if (hour < 12) {
+				return isLower ? "am" : "AM";
+			} else {
+				return isLower ? "pm" : "PM";
+			}
+		}
+	}
+};
+
+Date.prototype.format = function (formatString) {
+
+	var addPadding = function (value, length) {
+		var negative = ((value < 0) ? "-" : "");
+		var zeros = "0";
+		for (var i = 2; i < length; i++) {
+			zeros += "0";
+		}
+		return negative + (zeros + Math.abs(value).toString()).slice(-length);
+	};
+
+	var replacements = {
+		date: this,
+		YYYY: function () {
+			return this.date.getFullYear();
+		},
+		YY: function () {
+			return this.date.getFullYear() % 100;
+		},
+		MMMM: function () {
+			return this.date.getMonthName();
+		},
+		MMM: function () {
+			return this.date.getMonthNameShort();
+		},
+		MM: function () {
+			return addPadding((this.date.getMonth() + 1), 2);
+		},
+		M: function () {
+			return this.date.getMonth() + 1;
+		},
+		DDDD: function () {
+			return this.date.getDayName();
+		},
+		DDD: function () {
+			return this.date.getDayNameShort();
+		},
+		DD: function () {
+			return addPadding(this.date.getDate(), 2);
+		},
+		D: function () {
+			return this.date.getDate();
+		},
+		S: function () {
+			return this.date.getDateSuffix();
+		},
+		HH: function () {
+			return addPadding(this.date.getHours(), 2);
+		},
+		H: function () {
+			return this.date.getHours();
+		},
+		hh: function () {
+			var hour = this.date.getHours();
+			if (hour > 12) {
+				hour -= 12;
+			} else if (hour < 1) {
+				hour = 12;
+			}
+			return addPadding(hour, 2);
+		},
+		h: function () {
+			var hour = this.date.getHours();
+			if (hour > 12) {
+				hour -= 12;
+			} else if (hour < 1) {
+				hour = 12;
+			}
+			return hour;
+		},
+		mm: function () {
+			return addPadding(this.date.getMinutes(), 2);
+		},
+		m: function () {
+			return this.date.getMinutes();
+		},
+		ss: function () {
+			return addPadding(this.date.getSeconds(), 2);
+		},
+		s: function () {
+			return this.date.getSeconds();
+		},
+		fff: function () {
+			return addPadding(this.date.getMilliseconds(), 3);
+		},
+		ff: function () {
+			return addPadding(Math.floor(this.date.getMilliseconds() / 10), 2);
+		},
+		f: function () {
+			return Math.floor(this.date.getMilliseconds() / 100);
+		},
+		zzzz: function () {
+			return addPadding(Math.floor(-this.date.getTimezoneOffset() / 60), 2) + ":" + addPadding(-this.date.getTimezoneOffset() % 60, 2);
+		},
+		zzz: function () {
+			return Math.floor(-this.date.getTimezoneOffset() / 60) + ":" + addPadding(-this.date.getTimezoneOffset() % 60, 2);
+		},
+		zz: function () {
+			return addPadding(Math.floor(-this.date.getTimezoneOffset() / 60), 2);
+		},
+		z: function () {
+			return Math.floor(-this.date.getTimezoneOffset() / 60);
+		},
+		tt: function () {
+			return this.date.getMeridiem(true);
+		},
+		TT: function () {
+			return this.date.getMeridiem(false);
+		}
+	};
+
+
+	var formats = new Array();
+	while (formatString.length > 0) {
+		if (formatString[0] === "\"") {
+			var temp = /"[^"]*"/m.exec(formatString);
+			if (temp === null) {
+				formats.push(formatString.substring(1));
+				formatString = "";
+			} else {
+				temp = temp[0].substring(1, temp[0].length - 1);
+				formats.push(temp);
+				formatString = formatString.substring(temp.length + 2);
+			}
+		} else if (formatString[0] === "'") {
+			var temp = /'[^']*'/m.exec(formatString);
+			if (temp === null) {
+				formats.push(formatString.substring(1));
+				formatString = "";
+			} else {
+				temp = temp[0].substring(1, temp[0].length - 1);
+				formats.push(temp);
+				formatString = formatString.substring(temp.length + 2);
+			}
+		} else if (formatString[0] === "\\") {
+			if (formatString.length > 1) {
+				formats.push(formatString.substring(1, 2));
+				formatString = formatString.substring(2);
+			} else {
+				formats.push("\\");
+				formatString = "";
+			}
+		} else {
+			var foundMatch = false;
+			for (var i = formatString.length; i > 0; i--) {
+				if (formatString.substring(0, i) in replacements) {
+					formats.push(replacements[formatString.substring(0, i)]());
+					formatString = formatString.substring(i);
+					foundMatch = true;
+					break;
+				}
+			}
+			if (!foundMatch) {
+				formats.push(formatString[0]);
+				formatString = formatString.substring(1);
+			}
+		}
+	}
+
+	return formats.join("");
+};
\ No newline at end of file
diff --git a/web/root/pages/003_userlist.xjs b/web/root/pages/003_userlist.xjs
index ddb517f178..246e0e37e1 100644
--- a/web/root/pages/003_userlist.xjs
+++ b/web/root/pages/003_userlist.xjs
@@ -22,6 +22,7 @@
 
 	// Most people won't need to edit below this line
 	load('sbbsdefs.js');
+	load('js-date-format.js');
 
 	if (typeof http_request.query.offset === 'undefined' ||
 		isNaN(parseInt(http_request.query.offset[0])) ||
@@ -166,13 +167,26 @@
 		for (var u = 0; u < users.length; u++) {
 			writeln('<tr>');
 			for (var c in columns) {
-				writeln(
-					'<td>' +
-					(	columns[c].type === 'date'
-						? system.timestr(users[u][c])
-						: users[u][c].toString()
-					) +	'</td>'
-				);
+				var cellString = '';
+				if ( columns[c].type === 'date' ) {
+					// Generate JS dates
+					var theDate = new Date( users[u][c] * 1000);
+					var todayDate = new Date();
+					// Find how many days between last login date and today.
+					var diff = (Math.abs(theDate.getTime() - todayDate.getTime()) ) / (1000 * 60 * 60 * 24);
+					// If the date is within the last week, print just the day of the week
+					if (diff < 8) {
+						cellString = theDate.getDayName();
+					}
+					// Otherwise, print a date
+					else {
+						cellString = theDate.format('MMM D, YYYY');
+					}
+				}
+				else {
+					cellString = users[u][c].toString();
+				}
+				writeln( '<td>' + cellString + '</td>' );
 			}
 			writeln('</tr>');
 		}
-- 
GitLab


From 7b716676c65f9010dd9fa7924efe8ff35410f0e4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 18 Jan 2016 17:13:03 -0500
Subject: [PATCH 090/752] Include system.node_list[n].aux/extaux values in
 node-list status/action strings.

---
 web/root/api/system.ssjs | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index c7cbdaab76..3e80d1f498 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -16,11 +16,16 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			reply = system.node_list.map(
 				function (node) {
 					if (node.status === 3) var usr = new User(node.useron);
-					return ({
-						status : NodeStatus[node.status],
-						action : NodeAction[node.action],
-						user : (typeof usr === 'undefined' ? '' : usr.alias)
-					});
+					return (
+						{	status : format(
+								NodeStatus[node.status], node.aux, node.extaux
+							),
+							action : format(
+								NodeAction[node.action], node.aux, node.extaux
+							),
+							user : (typeof usr === 'undefined' ? '' : usr.alias)
+						}
+					);
 				}
 			);
 			var usr = new User(1);
@@ -89,4 +94,4 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 reply = JSON.stringify(reply);
 http_reply.header['Content-Type'] = 'application/json';
 http_reply.header['Content-Length'] = reply.length;
-write(reply);
\ No newline at end of file
+write(reply);
-- 
GitLab


From dcb525affc106eaf843a698f63e5745a9b27fdb4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 19 Jan 2016 15:14:23 -0500
Subject: [PATCH 091/752] Skip invalid xtrn sections read from settings file.

---
 web/root/pages/003-games.xjs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index 9452e5ca9b..8225f2f8cb 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -1,6 +1,6 @@
 <!--Games-->
 <?xjs 
-	if (typeof argv[0] != "boolean" || !argv[0]) exit();
+	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
 ?>
 
@@ -24,7 +24,7 @@
 	function launchXtrn(code) {
 		$.getJSON(
 			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function (data) {
+			function(data) {
 				fTelnet.Connect();
 			}
 		);
@@ -37,6 +37,7 @@
 
 	settings.xtrn_sections.forEach(
 		function (section) {
+			if (typeof xtrn_area.sec[section] === 'undefined') return;
 			writeln('<div class="list-group-item">');
 			writeln('<h4>' + xtrn_area.sec[section].name + '</h4>');
 			writeln('<ul class="nav nav-pills">');
@@ -45,9 +46,7 @@
 					writeln(
 						'<li role="presentation">' +
 						'<a href="#fTelnet" onclick="launchXtrn(\'' +
-							program.code + '\')">' +
-						program.name +
-						'</a>' +
+							program.code + '\')">' + program.name + '</a>' +
 						'</li>'
 					);
 				}
-- 
GitLab


From 63a62992225404602ccf1827d0f704cbeadbf79f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 19 Jan 2016 15:28:58 -0500
Subject: [PATCH 092/752] Never mind - existence of xtrn sec was already been
 tested for when loading settings.

---
 web/root/pages/003-games.xjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index 8225f2f8cb..e9b6c83477 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -37,7 +37,6 @@
 
 	settings.xtrn_sections.forEach(
 		function (section) {
-			if (typeof xtrn_area.sec[section] === 'undefined') return;
 			writeln('<div class="list-group-item">');
 			writeln('<h4>' + xtrn_area.sec[section].name + '</h4>');
 			writeln('<ul class="nav nav-pills">');
-- 
GitLab


From 71c6806fbd9e652d42c347a159c3e762391d75f8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 26 Feb 2016 13:05:16 -0500
Subject: [PATCH 093/752] Update README.md

It's websocket-telnet-service.js now, not websocketservice.js
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 16b1601532..225490effb 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ I haven't actually tried these instructions on a clean Synchronet BBS installati
 [WebSocket]
 Port=1123
 Options=NO_HOST_LOOKUP
-Command=websocketservice.js
+Command=websocket-telnet-service.js
 ```
 - Add the following section to your *ctrl/services.ini* file:
 ```ini
-- 
GitLab


From 769f3913df7fb2a89fb8cf71a22949ba4ea445eb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 28 Apr 2016 22:39:46 -0400
Subject: [PATCH 094/752] Use http_request.vhost instead of system.inet_addr
 for telnet/rlogin target hostname.

---
 web/root/pages/000-home.xjs  | 2 +-
 web/root/pages/003-games.xjs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index be9c7a4205..5744bffaa8 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -14,7 +14,7 @@
 	</div>
 </div>
 <script type="text/javascript">
-	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
+	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
 	fTelnet.Port = <?xjs write((webSocket === null) ? 1123 : webSocket.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index e9b6c83477..b54c04fedd 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -10,7 +10,7 @@
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 
 <script type="text/javascript">
-	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
+	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
 	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
-- 
GitLab


From 112b4d3705f57d872a20d444b1f7a74d88f62fbb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 16 Jun 2016 11:21:43 -0400
Subject: [PATCH 095/752] Use filebase.js instead of filedir.js.

---
 web/lib/files.js              | 31 +++++++++++++++----------------
 web/root/pages/002-files.ssjs | 26 +++++++++-----------------
 2 files changed, 24 insertions(+), 33 deletions(-)

diff --git a/web/lib/files.js b/web/lib/files.js
index e078fbdd47..1866cd2de8 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -1,35 +1,34 @@
-load('filedir.js');
+load('filebase.js');
 load('file_size.js');
 
 function listLibraries() {
-	var libraries = [];
-	file_area.lib_list.forEach(
-		function (library) {
-			if (library.dir_list.length > 0) libraries.push(library);
-		}
+	return file_area.lib_list.filter(
+		function (library) { return library.dir_list.length > 0; }
 	);
-	return libraries;
 }
 
 function listDirectories(library) {
 	var dirs = [];
 	file_area.lib_list[library].dir_list.forEach(
 		function (dir) {
-			var fd = new FileDir(dir);
-			if (fd.files.length < 1) return;
-			dirs.push({'dir' : dir, 'fileCount' : fd.files.length });
+			var fd = new FileBase(dir.code);
+			if (fd.length < 1) return;
+			dirs.push({'dir' : dir, 'fileCount' : fd.length });
 		}
 	);
 	return dirs;
 }
 
 function listFiles(dir) {
-	var files = [];
-	var fd = new FileDir(file_area.dir[dir]);
-	fd.files.forEach(
-		function (dirFile) {
-			dirFile.size = file_size_str(file_size(dirFile.fullPath));
-			files.push(dirFile);
+	var fd = new FileBase(file_area.dir[dir].code);
+	var files = fd.map(
+		function (df) {
+			if (typeof df.path !== 'undefined') {
+				df.size = file_size_str(file_size(df.path));
+			} else {
+				df.size = 'Unknown';
+			}
+			return df;
 		}
 	);
 	return files;
diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index b3f54b3b2c..299d05fc12 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -1,6 +1,7 @@
 //Files
 
-if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+if(typeof argv[0] !== 'boolean' || !argv[0])
+	exit();
 
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'files.js');
@@ -18,13 +19,10 @@ if (typeof http_request.query.dir !== 'undefined' &&
 		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
 		'</li>' +
 		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' +
-		file_area.dir[http_request.query.dir[0]].lib_index + '">' +
-		file_area.dir[http_request.query.dir[0]].lib_name + '</a>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + file_area.dir[http_request.query.dir[0]].lib_index + '">' + file_area.dir[http_request.query.dir[0]].lib_name + '</a>' +
 		'</li>' +
 		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;dir=' + 
-		http_request.query.dir[0] + '">' + http_request.query.dir[0] + '</a>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;dir=' + http_request.query.dir[0] + '">' + http_request.query.dir[0] + '</a>' +
 		'</li>' +
 		'</ol>'
 	);
@@ -32,8 +30,7 @@ if (typeof http_request.query.dir !== 'undefined' &&
 	function writeFileDetails(file) {
 		writeln(
 			format(
-				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" ' +
-				'target="_blank" class="list-group-item striped">' +
+				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
 				'<strong>%s</strong> (%s)' +
 				'<p><em>Uploaded %s</em></p>' +
 				'<p>%s</p>' +
@@ -43,12 +40,9 @@ if (typeof http_request.query.dir !== 'undefined' &&
 				file.name,
 				file.name,
 				file.size,
-				system.timestr(file.uploadDate),
-				file.description,
-				(	file.extendedDescription == ''
-					? ''
-					: ('<p>' + file.extendedDescription + '</p>')
-				)
+				system.timestr(file.uldate),
+				file.desc,
+				typeof file.extdesc === 'undefined' ? "" : ("<p>" + file.extdesc + "</p>")
 			)
 		);
 	}
@@ -70,9 +64,7 @@ if (typeof http_request.query.dir !== 'undefined' &&
 		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
 		'</li>' +
 		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' +
-		http_request.query.library[0] + '">' +
-		file_area.lib_list[http_request.query.library[0]].name + '</a>' +
+		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + http_request.query.library[0] + '">' + file_area.lib_list[http_request.query.library[0]].name + '</a>' +
 		'</li>' +
 		'</ol>'
 	);
-- 
GitLab


From 2e91623f1a134e5cd510bc1c16df80a36a867b76 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 16 Jun 2016 11:29:07 -0400
Subject: [PATCH 096/752] Replaced reference to filedir.js to filebase.js

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 225490effb..1a103d1c6a 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ A web interface for Synchronet BBS
 
 - This web interface has only been tested with Synchronet BBS 3.16c.  It will probably work with earlier and later versions.
 
-- The *Files* page of this web interface relies on a script which was introduced *after* the release of Synchronet BBS 3.16c.  You can grab a copy of *filedir.js* [here](http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/filedir.js?revision=1.2), and you should place it in your *exec/load/* directory.
+- The *Files* page of this web interface relies on a script which was introduced *after* the release of Synchronet BBS 3.16c.  You can grab a copy of *filebase.js* [here](http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/filebase.js?revision=1.7), and you should place it in your *exec/load/* directory.
 
 ###Quick start
 
-- 
GitLab


From e08bf4bf912cff676291d54b7a76fdbc0c2c0bfd Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 17 Jun 2016 18:48:42 -0400
Subject: [PATCH 097/752] Updated for filebase.js.  Kinda slow, but I will
 figure that out later.

---
 web/root/api/files.ssjs | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index f2c37f1413..22d8eac5c4 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,48 +1,48 @@
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'files.js');
-load('filedir.js');
+load('filebase.js');
 
 var reply = {};
 
 if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	typeof http_request.query.call !== 'undefined' &&
-	user.number > 0	&&
-	user.alias !== settings.guest
+	user.number > 0	&& user.alias !== settings.guest
 ) {
 
 	switch (http_request.query.call[0].toLowerCase()) {
 		case 'download-file':
-			reply = false;
-			if(	typeof http_request.query.dir !== 'undefined' &&
+			if (typeof http_request.query.dir !== 'undefined' &&
 				typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
 				file_area.dir[http_request.query.dir[0]].can_download &&
 				typeof http_request.query.file !== 'undefined'
 			) {
-				var fileDir = new FileDir(file_area.dir[http_request.query.dir[0]]);
+				var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
 				var file = null;
-				for (var f = 0; f < fileDir.files.length; f++) {
-					if (fileDir.files[f].name.toLowerCase() !==
-						http_request.query.file[0].toLowerCase()
-					) {
-						continue;
+				fileBase.some(
+					function (e) {
+						if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
+							return false;
+						} else if (typeof e.path !== 'undefined') {
+							file = e;
+							return true;
+						}
 					}
-					file = fileDir.files[f];
-					break;
-				}
+				);
 				if (file === null) break;
 				client.socket.send('HTTP/1.0 Status: 200 OK\r\n');
 				client.socket.send('Content-Type: application/octet-stream\r\n');
-				client.socket.send('Content-Disposition: attachment; filename="' + file.name + '";\r\n');
+				client.socket.send('Content-Disposition: attachment; filename="' + file.base + '.' + file.ext + '";\r\n');
 				client.socket.send('Content-Transfer-Encoding: binary\r\n');
-				client.socket.send('Content-Length: ' + file_size(file.fullPath) + '\r\n');
+				client.socket.send('Content-Length: ' + file_size(file.path) + '\r\n');
 				client.socket.send('\r\n');
-				var f = new File(file.fullPath);
+				var f = new File(file.path);
 				f.open('r+b');
 				while (!f.eof) {
 					client.socket.sendBin(f.readBin(1), 1);
 				}
 				f.close();
+				reply = false;
 			}
 			break;
 		default:
-- 
GitLab


From 4e0640366eacbbb74e44707b2c9a40f67db44f48 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:18:24 -0500
Subject: [PATCH 098/752] getSubUnreadCount: start at scan_ptr + 1 to avoid
 recounting last non-vote message, vote messages have made last_msg unreliable
 here; getMessageThreads: populate upvotes/downvotes properties for headers in
 thread arrays, or set to 0 if unavailable (backwards compatible); added
 voteMessage function to be called from ../root/api/forum.ssjs.

---
 web/lib/forum.js | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index edcfb1e559..b11a23a442 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -62,7 +62,7 @@ function getSubUnreadCount(sub) {
     try {
         var msgBase = new MsgBase(sub);
         msgBase.open();
-        for (var m = msg_area.sub[sub].scan_ptr; m < msgBase.last_msg; m++) {
+        for (var m = msg_area.sub[sub].scan_ptr + 1; m < msgBase.last_msg; m++) {
             var i = msgBase.get_msg_index(m);
             if (i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP) continue;
             if ((   msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY &&
@@ -402,6 +402,30 @@ function deleteMessage(sub, number) {
     return ret;
 }
 
+function voteMessage(sub, number, up) {
+    if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail') {
+        return false;
+    }
+    number = parseInt(number);
+    if (isNaN(number)) return false;
+    up = parseInt(up);
+    if (isNaN(up) || up < 0 || up > 1) return false;
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return false;
+    var header = msgBase.get_msg_header(number);
+    if (header === null) return false;
+    var vh = {
+        'from' : user.alias,
+        'from_ext' : user.number,
+        'from_net_type' : NET_NONE,
+        'thread_back' : header.number,
+        'attr' : up ? MSG_UPVOTE : MSG_DOWNVOTE
+    };
+    var ret = msgBase.vote_msg(vh);
+    msgBase.close();
+    return ret;
+}
+
 // Deuce's URL-ifier
 function linkify(body) {
     urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
@@ -597,7 +621,9 @@ var getMessageThreads = function(sub, max) {
             from : header.from,
             from_net_addr : header.from_net_addr,
             to : header.to,
-            when_written_time : header.when_written_time
+            when_written_time : header.when_written_time,
+            upvotes : header.upvotes || 0,
+            downvotes : header.downvotes || 0
         };
     }
 
-- 
GitLab


From bfb19ccddf70dcf803efafb3c4aa946692994860 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:19:56 -0500
Subject: [PATCH 099/752] Added upvote/downvote indicators and buttons in
 thread view.

---
 web/root/pages/001-forum.ssjs | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 5893fd8437..682c855242 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -48,6 +48,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		}
 
 		// Header
+		writeln('<div class="row">');
+
+		writeln('<div class="col-sm-8">');
 		if (user.alias != settings.guest &&
 			header.number > msg_area.sub[http_request.query.sub[0]].scan_ptr
 		) {
@@ -65,6 +68,28 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			' to <strong>' + header.to + '</strong> on ' +
 			(new Date(header.when_written_time * 1000)).toLocaleString()
 		);
+		writeln('</div>');
+		
+		writeln(
+			'<div class="col-sm-4">' +
+				'<div class="pull-right">' +
+				'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon">' +
+					'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
+					'</span>' +
+					'<span id="uv-count-' + header.number + '" title="Upvotes">' +
+					header.upvotes + '</span>' +
+				'</button>' +
+				'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon">' +
+					'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
+					'</span>' +
+					'<span id="dv-count-' + header.number + '" title="Downvotes">' +
+					header.downvotes + '</span>' +
+				'</button>' +
+				'</div>' +
+			'</div>'
+		);
+		
+		writeln('</div>'); // message header
 
 		// Body
 		writeln(
@@ -213,7 +238,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		log(LOG_WARNING, err);
 	}
 
-	writeln('<script type="text/javascript">threadNav();</script>');
+	writeln(
+		'<script type="text/javascript">' +
+		'threadNav();' +
+		'enableVoteButtonHandlers("' + http_request.query.sub[0] + '");' +
+		'</script>'
+	);
 
 } else if (
 	typeof http_request.query.sub !== 'undefined' &&
-- 
GitLab


From 8b819c946a858859afc2fcf041ddb02a7708e84a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:21:50 -0500
Subject: [PATCH 100/752] Added enableVoteButtonHandlers function and vote
 callback function.

---
 web/root/js/forum.js | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 13ea6ed3ef..2f28e562da 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -295,4 +295,32 @@ function threadNav() {
 		}
 	);
 
+}
+
+function vote(sub, id) {
+	id = id.split('-');
+	if (id.length !== 2 ||
+		(id[0] !== 'uv' && id[0] !== 'dv') ||
+		isNaN(parseInt(id[1]))
+	) {
+		return;
+	}
+	$.getJSON(
+		'./api/forum.ssjs?call=vote&sub=' + sub + '&id=' + id[1] + '&up=' + (id[0] === 'uv' ? 1 : 0),
+		function (data) {
+			if (!data.success) return;
+			$('#' + id[0] + '-' + id[1]).addClass(
+				id[0] === 'uv' ? 'btn-uv-voted' : 'btn-dv-voted'
+			);
+			$('#' + id[0] + '-' + id[1]).attr('disabled', true);
+			$('#' + id[0] + '-' + id[1]).blur();
+			var count = parseInt($('#' + id[0] + '-count-' + id[1]).text()) + 1;
+			$('#' + id[0] + '-count-' + id[1]).text(count);
+		}
+	);
+}
+
+function enableVoteButtonHandlers(sub) {
+	$('.btn-uv').click(function () { vote(sub, this.id); });
+	$('.btn-dv').click(function () { vote(sub, this.id); });
 }
\ No newline at end of file
-- 
GitLab


From d7ca412278d5878bcdc3bce5b0e6bf4e95c77d14 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:23:35 -0500
Subject: [PATCH 101/752] Don't display vote buttons to guest user for now.

---
 web/root/pages/001-forum.ssjs | 38 ++++++++++++++++++-----------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 682c855242..d08b5a3925 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -70,24 +70,26 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		);
 		writeln('</div>');
 		
-		writeln(
-			'<div class="col-sm-4">' +
-				'<div class="pull-right">' +
-				'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon">' +
-					'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
-					'</span>' +
-					'<span id="uv-count-' + header.number + '" title="Upvotes">' +
-					header.upvotes + '</span>' +
-				'</button>' +
-				'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon">' +
-					'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
-					'</span>' +
-					'<span id="dv-count-' + header.number + '" title="Downvotes">' +
-					header.downvotes + '</span>' +
-				'</button>' +
-				'</div>' +
-			'</div>'
-		);
+		if (user.alias != settings.guest) {
+			writeln(
+				'<div class="col-sm-4">' +
+					'<div class="pull-right">' +
+					'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon">' +
+						'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
+						'</span>' +
+						'<span id="uv-count-' + header.number + '" title="Upvotes">' +
+						header.upvotes + '</span>' +
+					'</button>' +
+					'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon">' +
+						'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
+						'</span>' +
+						'<span id="dv-count-' + header.number + '" title="Downvotes">' +
+						header.downvotes + '</span>' +
+					'</button>' +
+					'</div>' +
+				'</div>'
+			);
+		}
 		
 		writeln('</div>'); // message header
 
-- 
GitLab


From 648ee189d675cb2a7c2b3e843b9f2795a1df1d36 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:24:37 -0500
Subject: [PATCH 102/752] Added handler for 'vote' API call

---
 web/root/api/forum.ssjs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 7a3f1b005c..321a41ae31 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -125,6 +125,22 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 reply.success = false;
             }
             break;
+
+        case 'vote':
+            if (typeof http_request.query.sub !== 'undefined' &&
+                typeof http_request.query.id !== 'undefined' &&
+                typeof http_request.query.up !== 'undefined' &&
+                !(user.security.restrictions&UFLAG_V)
+            ) {
+                reply.success = voteMessage(
+                    http_request.query.sub[0],
+                    http_request.query.id[0],
+                    http_request.query.up[0]
+                );
+            } else {
+                reply.success = false;
+            }
+            break;
         
         default:
             break;
-- 
GitLab


From eed3674c8ce79fc98533b45c6464348a4b0b631d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:25:26 -0500
Subject: [PATCH 103/752] Added some vote button related rules

---
 web/root/css/style.css | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 4326867de5..28e36cbfe3 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -70,4 +70,24 @@ input.dropdown {
 
 .reply {
 	margin: 1em 0 1em 0;
+}
+
+/* Upvote button */
+.btn-uv {
+
+}
+
+/* Downvote button */
+.btn-dv {
+
+}
+
+/* Upvote button (user has already upvoted) */
+.btn-uv-voted, .btn-uv-voted:hover {
+	color : orange;
+}
+
+/* Downvote button (user has already downvoted) */
+.btn-dv-voted, .btn-dv-voted:hover {
+	color : blue;
 }
\ No newline at end of file
-- 
GitLab


From c21ff670fc15dbe7b162dc0e8fdda383c6af75c8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:33:17 -0500
Subject: [PATCH 104/752] Show vote indicators to guests, but disable buttons &
 do not enable click callbacks.

---
 web/root/pages/001-forum.ssjs | 49 ++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 26 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index d08b5a3925..2aef80777e 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -70,26 +70,24 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		);
 		writeln('</div>');
 		
-		if (user.alias != settings.guest) {
-			writeln(
-				'<div class="col-sm-4">' +
-					'<div class="pull-right">' +
-					'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon">' +
-						'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
-						'</span>' +
-						'<span id="uv-count-' + header.number + '" title="Upvotes">' +
-						header.upvotes + '</span>' +
-					'</button>' +
-					'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon">' +
-						'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
-						'</span>' +
-						'<span id="dv-count-' + header.number + '" title="Downvotes">' +
-						header.downvotes + '</span>' +
-					'</button>' +
-					'</div>' +
-				'</div>'
-			);
-		}
+		writeln(
+			'<div class="col-sm-4">' +
+				'<div class="pull-right">' +
+				'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
+					'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
+					'</span>' +
+					'<span id="uv-count-' + header.number + '" title="Upvotes">' +
+					header.upvotes + '</span>' +
+				'</button>' +
+				'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
+					'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
+					'</span>' +
+					'<span id="dv-count-' + header.number + '" title="Downvotes">' +
+					header.downvotes + '</span>' +
+				'</button>' +
+				'</div>' +
+			'</div>'
+		);
 		
 		writeln('</div>'); // message header
 
@@ -240,12 +238,11 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		log(LOG_WARNING, err);
 	}
 
-	writeln(
-		'<script type="text/javascript">' +
-		'threadNav();' +
-		'enableVoteButtonHandlers("' + http_request.query.sub[0] + '");' +
-		'</script>'
-	);
+	writeln('<script type="text/javascript">threadNav();');
+	if (user.alias != settings.guest) {
+		writeln('enableVoteButtonHandlers("'+http_request.query.sub[0]+'");');
+	}
+	writeln('</script>');
 
 } else if (
 	typeof http_request.query.sub !== 'undefined' &&
-- 
GitLab


From d490d7ad81b29167d59a7195fe5ffb08025fd870 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:39:47 -0500
Subject: [PATCH 105/752] Added settings toggle for voting functions.

---
 web/root/pages/001-forum.ssjs | 40 ++++++++++++++++++-----------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 2aef80777e..53d6f4be3d 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -69,25 +69,27 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			(new Date(header.when_written_time * 1000)).toLocaleString()
 		);
 		writeln('</div>');
-		
-		writeln(
-			'<div class="col-sm-4">' +
-				'<div class="pull-right">' +
-				'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
-					'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
-					'</span>' +
-					'<span id="uv-count-' + header.number + '" title="Upvotes">' +
-					header.upvotes + '</span>' +
-				'</button>' +
-				'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
-					'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
-					'</span>' +
-					'<span id="dv-count-' + header.number + '" title="Downvotes">' +
-					header.downvotes + '</span>' +
-				'</button>' +
-				'</div>' +
-			'</div>'
-		);
+
+		if (typeof settings.vote_buttons === 'undefined' || settings.vote_buttons) {		
+			writeln(
+				'<div class="col-sm-4">' +
+					'<div class="pull-right">' +
+					'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
+						'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
+						'</span>' +
+						'<span id="uv-count-' + header.number + '" title="Upvotes">' +
+						header.upvotes + '</span>' +
+					'</button>' +
+					'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
+						'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
+						'</span>' +
+						'<span id="dv-count-' + header.number + '" title="Downvotes">' +
+						header.downvotes + '</span>' +
+					'</button>' +
+					'</div>' +
+				'</div>'
+			);
+		}
 		
 		writeln('</div>'); // message header
 
-- 
GitLab


From 5be241b8244ea18c3a5fb22b635489348efcc7d4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:41:35 -0500
Subject: [PATCH 106/752] Disable vote buttons if user has REST V

---
 web/root/pages/001-forum.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 53d6f4be3d..063ae555ba 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -74,13 +74,13 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			writeln(
 				'<div class="col-sm-4">' +
 					'<div class="pull-right">' +
-					'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
+					'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon"' + (user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '') + '>' +
 						'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
 						'</span>' +
 						'<span id="uv-count-' + header.number + '" title="Upvotes">' +
 						header.upvotes + '</span>' +
 					'</button>' +
-					'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon"' + (user.alias == settings.guest ? 'disabled' : '') + '>' +
+					'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon"' + (user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '') + '>' +
 						'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
 						'</span>' +
 						'<span id="dv-count-' + header.number + '" title="Downvotes">' +
-- 
GitLab


From e6ad511d22133a9f3992cb251cfd0fd8eede2e7b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:42:16 -0500
Subject: [PATCH 107/752] Disable vote buttons if user has REST V

---
 web/root/pages/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 063ae555ba..dfdd987145 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -241,7 +241,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	}
 
 	writeln('<script type="text/javascript">threadNav();');
-	if (user.alias != settings.guest) {
+	if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
 		writeln('enableVoteButtonHandlers("'+http_request.query.sub[0]+'");');
 	}
 	writeln('</script>');
-- 
GitLab


From d0f9a6eb87788cd6a44114f6d0f7f6bf660ccc13 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 18:54:21 -0500
Subject: [PATCH 108/752] Minor updates and support information.

---
 README.md | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index 1a103d1c6a..121e56cc1e 100644
--- a/README.md
+++ b/README.md
@@ -5,21 +5,15 @@ A web interface for Synchronet BBS
 
 - Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
 
-- This readme kind of sucks.  I'll put a better one on the Synchronet Wiki once I'm ready to bring this over to the Synchronet CVS repository.
-	- Things may change quite a bit by the time I do bring this web interface over to the CVS, so try not to get too attached to any customizations that you make in the meantime.
-		- However, if you're an early adopter, I would appreciate your feedback.
-
 ###Requirements
 
-- This web interface has only been tested with Synchronet BBS 3.16c.  It will probably work with earlier and later versions.
+- This web interface has been tested with Synchronet BBS 3.16c and 3.17.  It will probably work with earlier and later versions.
 
 - The *Files* page of this web interface relies on a script which was introduced *after* the release of Synchronet BBS 3.16c.  You can grab a copy of *filebase.js* [here](http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/filebase.js?revision=1.7), and you should place it in your *exec/load/* directory.
 
 ###Quick start
 
-I haven't actually tried these instructions on a clean Synchronet BBS installation, but they should work.
-
-- Back up your Synchronet installation
+- Back up your Synchronet installation (particularly 'ctrl' and 'web')
 - Shut down your BBS
 - Clone [or download an archive of](https://github.com/echicken/synchronet-web-v4/archive/master.zip) this repository to a convenient location
 	- Copy the contents of the downloaded *mods* directory into your local *mods* directory
@@ -54,10 +48,12 @@ I haven't actually tried these instructions on a clean Synchronet BBS installati
 	; Only load this many messages from each sub (default: 0 for all)
 	; (If you get 'Out of memory' errors when viewing subs, tweak this setting)
 	max_messages = 0
+	; Display upvote/downvote buttons in message threads (3.17)
+	vote_buttons = true
 ```
-- Add the following section to your *ctrl/services.ini* file if it isn't there already:
+- Add the following section to your *ctrl/services.ini* file:
 ```ini
-[WebSocket]
+[WebSocketTelnet]
 Port=1123
 Options=NO_HOST_LOOKUP
 Command=websocket-telnet-service.js
@@ -117,3 +113,22 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 - The [web] section added to *ctrl/modopts.ini* won't hurt anything if you leave it there, but you can delete it if you want
 - Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
 - Undo any changes you made to your firewall & router during the *Quick Start*
+
+###Support
+
+####Via GitHub
+
+- Please browse the existing [issues](https://github.com/echicken/synchronet-web-v4/issues) for this project, including those marked as closed.  You may find that your question has already been asked (and hopefully answered).
+- Open a new [issue](https://github.com/echicken/synchronet-web-v4/issues) here on GitHub
+
+####On DOVE-Net
+
+- Post a message to *echicken* in [Synchronet Sysops](https://bbs.electronicchicken.com/?page=001-forum.ssjs&sub=sync_sys).  I read this sub regularly and will respond to you there.
+
+####On IRC
+
+- You can find me in #synchronet on irc.synchro.net.  I may be AFK, but ask your question and idle for a while; I'll respond eventually.
+
+####Email
+
+- Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
\ No newline at end of file
-- 
GitLab


From 3f66a2bc24b30bb42419fab85cb3093915171b74 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 23:36:20 -0500
Subject: [PATCH 109/752] Tally thread total upvotes and downvotes in
 getMessageThreads, available in threads.thread[thread].votes{up,down}.

---
 web/lib/forum.js | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index b11a23a442..0fc7509361 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -625,6 +625,8 @@ var getMessageThreads = function(sub, max) {
             upvotes : header.upvotes || 0,
             downvotes : header.downvotes || 0
         };
+        threads.thread[thread_id].votes.up += (header.upvotes || 0);
+        threads.thread[thread_id].votes.down += (header.downvotes || 0);
     }
 
     function getSomeMessageHeaders(msgBase, start, count) {
@@ -690,7 +692,11 @@ var getMessageThreads = function(sub, max) {
                         id : headers[h].thread_id,
                         newest : 0,
                         subject : headers[h].subject,
-                        messages : {}
+                        messages : {},
+                        votes : {
+                            up : 0,
+                            down : 0
+                        }
                     };
                     addToThread(headers[h].thread_id, headers[h], subject);
                 }
@@ -734,7 +740,11 @@ var getMessageThreads = function(sub, max) {
                     id : headers[h].number,
                     newest : 0,
                     subject : headers[h].subject,
-                    messages : {}
+                    messages : {},
+                    votes : {
+                        up : 0,
+                        down : 0
+                    }
                 };
                 addToThread(headers[h].number, headers[h], subject);
 
-- 
GitLab


From 9b09b13a088e85c877500b36352a42c715453e13 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 23:37:03 -0500
Subject: [PATCH 110/752] Display upvote & downvote counts in thread view for
 parent message & cumulative votes on all messages in the thread.

---
 web/root/pages/001-forum.ssjs | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index dfdd987145..d7a27070d5 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -265,16 +265,20 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		writeln(
 			format(
 				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" ' +
-				'class="list-group-item striped">' +
+				'class="list-group-item striped">',
+				http_request.query.page[0],
+				http_request.query.sub[0],
+				thread.id
+			)
+		);
+		writeln('<div class="row"><div class="col-sm-8">');
+		writeln(
+			format(
 				'<strong>%s</strong>' +
 				'<p>By <strong>%s</strong> on %s</p>' +
 				'<span title="Unread messages" class="badge%s" ' +
 				'id="badge-%s">%s</span>' +
-				'<p>Latest reply by <strong>%s</strong> on %s</p>' +
-				'</a>',
-				http_request.query.page[0],
-				http_request.query.sub[0],
-				thread.id,
+				'<p>Latest reply by <strong>%s</strong> on %s</p>',
 				thread.subject,
 				thread.messages[first].from,
 				(new Date(
@@ -293,6 +297,24 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				)).toLocaleString()
 			)
 		);
+		writeln('</div>');
+		if (settings.vote_buttons) {
+			writeln(
+				'<div class="col-sm-4"><div class="pull-right">' +
+				'<div title="Upvotes - Parent (Thread Total)" class="btn icon">' +
+				'<span class="glyphicon glyphicon-arrow-up">' +
+				'</span>' +
+				thread.messages[first].upvotes + ' (' + thread.votes.up + ')' +
+				'</div>' +
+				'<div title="Downvotes - Parent (Thread Total)" class="btn icon">' +
+				'<span class="glyphicon glyphicon-arrow-down">' +
+				'</span>' +
+				thread.messages[first].downvotes + ' (' + thread.votes.down + ')' +
+				'</div>' +
+				'</div></div>'
+			);
+		}
+		writeln('</div></a>');
 	}
 
 	writeln(
-- 
GitLab


From 029c34b51a820b7c8f3555fa33683acb7b36f96a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 23:41:40 -0500
Subject: [PATCH 111/752] Make keyboard navigation in message threads a config
 toggle (I've decided that I hate it)

---
 README.md                     | 2 ++
 web/root/pages/001-forum.ssjs | 5 ++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 121e56cc1e..ca585ccdaa 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,8 @@ A web interface for Synchronet BBS
 	; Only load this many messages from each sub (default: 0 for all)
 	; (If you get 'Out of memory' errors when viewing subs, tweak this setting)
 	max_messages = 0
+	; Enable or disable keyboard navigation in message threads
+	keyboard_navigation = false
 	; Display upvote/downvote buttons in message threads (3.17)
 	vote_buttons = true
 ```
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index d7a27070d5..266032a87a 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -240,7 +240,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		log(LOG_WARNING, err);
 	}
 
-	writeln('<script type="text/javascript">threadNav();');
+	writeln('<script type="text/javascript">');
+	if (settings.keyboard_navigation) {
+		writeln('threadNav();');
+	}
 	if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
 		writeln('enableVoteButtonHandlers("'+http_request.query.sub[0]+'");');
 	}
-- 
GitLab


From 56427936fdf8c7a013cb3372bf7b88599a5b1d65 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 16 Nov 2016 23:43:13 -0500
Subject: [PATCH 112/752] Point of clarification re 'inactivity' setting; it's
 for how long a web user may remain 'idle' before disappearing from the Who's
 Online list

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index ca585ccdaa..52d89136dd 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ A web interface for Synchronet BBS
 	guest = Guest
 	; Login sessions expire after this many seconds of inactivity
 	timeout = 43200
-	; Users disappear from the "Who's online" list after this many seconds
+	; Web-based users disappear from the "Who's online" list after this many seconds
 	inactivity = 900
 	; Allow new users to register via the web interface
 	user_registration = true
-- 
GitLab


From adebd275acd8b534a2eb4e74d63c13c3aeed5974 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 17 Nov 2016 00:14:30 -0500
Subject: [PATCH 113/752] Align the unread indicator with the vote buttons in
 thread-list view; will tweak this more later.

---
 web/root/pages/001-forum.ssjs | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 266032a87a..511b0d4bf2 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -279,8 +279,6 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			format(
 				'<strong>%s</strong>' +
 				'<p>By <strong>%s</strong> on %s</p>' +
-				'<span title="Unread messages" class="badge%s" ' +
-				'id="badge-%s">%s</span>' +
 				'<p>Latest reply by <strong>%s</strong> on %s</p>',
 				thread.subject,
 				thread.messages[first].from,
@@ -288,12 +286,6 @@ if (typeof http_request.query.sub !== 'undefined' &&
 						thread.messages[first].when_written_time * 1000
 					)
 				).toLocaleString(),
-				(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW
-					? ' scanned'
-					: ''
-				),
-				thread.messages[first].number,
-				(unread == 0 ? "" : unread),
 				thread.messages[last].from,
 				(new Date(
 					thread.messages[last].when_written_time * 1000
@@ -301,9 +293,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			)
 		);
 		writeln('</div>');
+		writeln(
+			'<div class="col-sm-4">' +
+			'<div class="pull-right" style="text-align:right;">'
+		);
 		if (settings.vote_buttons) {
 			writeln(
-				'<div class="col-sm-4"><div class="pull-right">' +
 				'<div title="Upvotes - Parent (Thread Total)" class="btn icon">' +
 				'<span class="glyphicon glyphicon-arrow-up">' +
 				'</span>' +
@@ -313,10 +308,22 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				'<span class="glyphicon glyphicon-arrow-down">' +
 				'</span>' +
 				thread.messages[first].downvotes + ' (' + thread.votes.down + ')' +
-				'</div>' +
-				'</div></div>'
+				'</div>'
 			);
 		}
+		writeln(
+			format(
+				'<div class="btn icon">' +
+				'<span title="Unread messages" class="badge%s" id="badge-%s">' +
+				'%s</span></div>',
+				(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW
+					? ' scanned' : ''
+				),
+				thread.messages[first].number,
+				(unread == 0 ? '' : unread)
+			)
+		);
+		writeln('</div></div>');
 		writeln('</div></a>');
 	}
 
-- 
GitLab


From af7b8d4663bbd423cb66df5f2c7bff71b43bf193 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 17 Nov 2016 00:15:17 -0500
Subject: [PATCH 114/752] Populate threads.thread[thread].votes in yet another
 spot (looks like the last one).

---
 web/lib/forum.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 0fc7509361..b603db8972 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -724,7 +724,11 @@ var getMessageThreads = function(sub, max) {
                             id : headers[h].thread_back,
                             newest : 0,
                             subject : headers[h].subject,
-                            messages : {}
+                            messages : {},
+                            votes : {
+                                up : 0,
+                                down : 0
+                            }
                         };
                         addToThread(
                             headers[h].thread_back,
-- 
GitLab


From f3df9c172ff03e08d438a2e4b2e0e13d44b3286f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 17 Nov 2016 00:21:29 -0500
Subject: [PATCH 115/752] More better

---
 web/root/pages/001-forum.ssjs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 511b0d4bf2..0160da14e5 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -293,12 +293,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			)
 		);
 		writeln('</div>');
-		writeln(
-			'<div class="col-sm-4">' +
-			'<div class="pull-right" style="text-align:right;">'
-		);
 		if (settings.vote_buttons) {
 			writeln(
+				'<div class="col-sm-3">' +
+				'<div class="pull-right">' +
 				'<div title="Upvotes - Parent (Thread Total)" class="btn icon">' +
 				'<span class="glyphicon glyphicon-arrow-up">' +
 				'</span>' +
@@ -308,14 +306,17 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				'<span class="glyphicon glyphicon-arrow-down">' +
 				'</span>' +
 				thread.messages[first].downvotes + ' (' + thread.votes.down + ')' +
+				'</div>' +
+				'</div>' +
 				'</div>'
 			);
 		}
 		writeln(
 			format(
+				'<div class="col-sm-1">' +
 				'<div class="btn icon">' +
 				'<span title="Unread messages" class="badge%s" id="badge-%s">' +
-				'%s</span></div>',
+				'%s</span></div></div>',
 				(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW
 					? ' scanned' : ''
 				),
@@ -323,7 +324,6 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				(unread == 0 ? '' : unread)
 			)
 		);
-		writeln('</div></div>');
 		writeln('</div></a>');
 	}
 
-- 
GitLab


From 6e18e7e2248eaca6bd4ae7d887dc107f038a2e37 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 18:54:08 -0500
Subject: [PATCH 116/752] Various added bits of support for votes and polls;
 still a work in progress.

---
 web/lib/forum.js | 131 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 129 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index b603db8972..9cc48856a7 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -114,6 +114,93 @@ function getUnreadInThread(sub, thread) {
     return count;
 }
 
+function getVotesInThread(sub, thread) {
+    var ret = { t : { u : 0, d : 0 }, m : {} };
+    if (typeof msg_area.sub[sub] === 'undefined') return ret;
+    if (typeof thread === 'number') {
+        var threads = getMessageThreads(sub, settings.max_messages);
+        if (typeof threads.thread[thread] === 'undefined') return ret;
+        thread = threads.thread[thread];
+    }
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return ret;
+    Object.keys(thread.messages).forEach(
+        function (m) {
+            if (thread.messages[m].upvotes > 0 ||
+                thread.messages[m].downvotes > 0
+            ) {
+                ret.t.up += thread.messages[m].upvotes;
+                ret.t.down += thread.messages[m].downvotes;
+                ret.m[thread.messages[m].number] = {
+                    u : thread.messages[m].upvotes,
+                    d : thread.messages[m].downvotes,
+                    v : msgBase.how_user_voted(
+                        thread.messages[m].number,
+                        msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
+                    )
+                };
+            }
+        }
+    );
+    msgBase.close();
+    return ret;
+}
+
+function getVotesInThreads(sub) {
+    var threads = getMessageThreads(sub, settings.max_messages);
+    var ret = {};
+    Object.keys(threads.thread).forEach(
+        function(t) {
+            Object.keys(threads.thread[t].messages).forEach(
+                function (m, i) {
+                    if (threads.thread[t].messages[m].upvotes < 1 &&
+                        threads.thread[t].messages[m].downvotes < 1
+                    ) {
+                        return;
+                    }
+                    if (typeof ret[t] === 'undefined') {
+                        ret[t] = { p : { u : 0, d : 0 }, t : { u : 0, d : 0 } };
+                        if (i < 1) {
+                            ret[t].p.u = threads.thread[t].messages[m].upvotes;
+                            ret[t].p.d = threads.thread[t].messages[m].downvotes;
+                        }
+                    }
+                    ret[t].t.u += threads.thread[t].messages[m].upvotes;
+                    ret[t].t.d += threads.thread[t].messages[m].downvotes;
+                }
+            );
+        }
+    );
+    return ret;
+}
+
+function getUserPollData(sub, id) {
+    var ret = { answers : 0, show_results : false };
+    if (typeof msg_area.sub[sub] === 'undefined') return ret;
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return ret;
+    var header = msgBase.get_msg_header(id);
+    if (header === null || !(header.attr&MSG_POLL)) {
+        msgBase.close();
+        return ret;
+    }
+    ret.answers = msgBase.how_user_voted(
+        header.number,
+        msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
+    );
+    msgBase.close();
+    if (header.from === user.alias || header.from === user.name) {
+        ret.show_results = true;
+    } else if (header.auxattr&POLL_RESULTS_CLOSED && header.auxattr&POLL_CLOSED) {
+        ret.show_results = true;
+    } else if (header.auxattr&POLL_RESULTS_OPEN) {
+        ret.show_results = true;
+    } else if (header.auxattr&POLL_RESULTS_VOTERS && ret.answers > 0) {
+        ret.show_results = true;
+    }
+    return ret;
+}
+
 function getMailUnreadCount() {
     var count = 0;
     var msgBase = new MsgBase('mail');
@@ -426,6 +513,13 @@ function voteMessage(sub, number, up) {
     return ret;
 }
 
+function submitPollAnswers(sub, number, answers) {
+    // verify that user can vote
+    // verify that the poll is open
+    // msgbase.vote_msg
+    return true;
+}
+
 // Deuce's URL-ifier
 function linkify(body) {
     urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
@@ -613,10 +707,15 @@ var getMessageThreads = function(sub, max) {
     var threads = { thread : {}, order : [] };
     var subjects = {};
 
+    if (typeof msg_area.sub[sub] === 'undefined') return threads;
+    if (!msg_area.sub[sub].can_read) return threads;
+
     function addToThread(thread_id, header, subject) {
         if (typeof subject !== 'undefined') subjects[subject] = thread_id;
         threads.thread[thread_id].newest = header.when_written_time;
         threads.thread[thread_id].messages[header.number] = {
+            attr : header.attr,
+            auxattr : header.auxattr,
             number : header.number,
             from : header.from,
             from_net_addr : header.from_net_addr,
@@ -625,8 +724,36 @@ var getMessageThreads = function(sub, max) {
             upvotes : header.upvotes || 0,
             downvotes : header.downvotes || 0
         };
-        threads.thread[thread_id].votes.up += (header.upvotes || 0);
-        threads.thread[thread_id].votes.down += (header.downvotes || 0);
+        if (header.attr&MSG_POLL) {
+            header.field_list.sort(
+                function (a, b) {
+                    if (a.type === 0x62) return -1;
+                    if (b.type === 0x62) return 1;
+                    return 0;
+                }
+            );
+            threads.thread[thread_id].messages[header.number].poll_comments = [];
+            threads.thread[thread_id].messages[header.number].poll_answers = [];
+            header.field_list.forEach(
+                function (e) {
+                    switch (e.type) {
+                        case SMB_COMMENT:
+                            threads.thread[thread_id].messages[header.number].poll_comments.push(e);
+                            break;
+                        case SMB_POLL_ANSWER:
+                            threads.thread[thread_id].messages[header.number].poll_answers.push(e);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            );
+            threads.thread[thread_id].messages[header.number].votes = header.votes;
+            threads.thread[thread_id].messages[header.number].tally = header.tally;
+        } else {
+            threads.thread[thread_id].votes.up += (header.upvotes || 0);
+            threads.thread[thread_id].votes.down += (header.downvotes || 0);
+        }
     }
 
     function getSomeMessageHeaders(msgBase, start, count) {
-- 
GitLab


From be344fc36c8f893b490f4579359ef56cc221e61b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 18:55:21 -0500
Subject: [PATCH 117/752] Poll answer submission handler, async thread/sub vote
 calls.

---
 web/root/api/forum.ssjs | 309 +++++++++++++++++++++++-----------------
 1 file changed, 178 insertions(+), 131 deletions(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 321a41ae31..c203ae023a 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -11,140 +11,187 @@ var reply = {};
 
 // There must be an API call, and the user must not be a guest or unknown
 if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-    typeof http_request.query.call !== 'undefined' &&
-    user.number > 0 &&
-    user.alias !== settings.guest
+    typeof http_request.query.call !== 'undefined'
 ) {
 
-    switch(http_request.query.call[0].toLowerCase()) {
-
-        case 'list-groups':
-            reply = listGroups();
-            break;
-        
-        case 'list-subs':
-            if (typeof http_request.query.group !== 'undefined') {
-                reply = listSubs(http_request.query.group[0]);
-            }
-            break;
-        
-        case 'list-threads':
-            if (typeof http_request.query.sub !== 'undefined') {
-                reply = listThreads(http_request.query.sub[0]);
-            }
-            break;
-        
-        case 'get-group-unread-count':
-            if (typeof http_request.query.group !== 'undefined') {
-                http_request.query.group.forEach(
-                    function(group) {
-                        reply[group] = getGroupUnreadCount(group);
-                    }
-                );
-            }
-            break;
-
-        case 'get-sub-unread-count':
-            if (typeof http_request.query.sub !== 'undefined') {
-                http_request.query.sub.forEach(
-                    function(sub) {
-                        reply[sub] = getSubUnreadCount(sub);
-                    }
-                );
-            }
-            break;
-
-        case 'get-mail-unread-count':
-            reply.count = getMailUnreadCount();
-            break;
-        
-        case 'get-mail-body':
-            if (typeof http_request.query.number !== 'undefined') {
-                reply = getMailBody(http_request.query.number[0]);
-            }
-            break;
-        
-        case 'get-signature':
-            reply.signature = getSignature();
-            break;
-        
-        case 'post-reply':
-            if (typeof http_request.query.sub !== 'undefined' &&
-                typeof http_request.query.body !== 'undefined' &&
-                typeof http_request.query.pid !== 'undefined'
-            ) {
-                reply.success = postReply(
-                    http_request.query.sub[0],
-                    http_request.query.body[0],
-                    Number(http_request.query.pid[0])
-                );
-            } else {
-                reply.success = false;
-            }
-            break;
-        
-        case 'post':
-            if (typeof http_request.query.sub !== 'undefined' &&
-                typeof http_request.query.to !== 'undefined' &&
-                typeof http_request.query.subject !== 'undefined' &&
-                typeof http_request.query.body !== 'undefined'
-            ) {
-                reply.success = postNew(
-                    http_request.query.sub[0],
-                    http_request.query.to[0],
-                    http_request.query.subject[0],
-                    http_request.query.body[0]
-                );
-            } else {
-                reply.success = false;
-            }
-            break;
-        
-        case 'delete-message':
-            if (typeof http_request.query.sub !== 'undefined' &&
-                typeof http_request.query.number !== 'undefined'
-            ) {
-                reply.success = deleteMessage(
-                    http_request.query.sub[0],
-                    http_request.query.number[0]
-                );
-            } else {
-                reply.success = false;
-            }
-            break;
-
-        case 'set-scan-cfg':
-            if (typeof http_request.query.sub !== 'undefined' &&
-                typeof http_request.query.cfg !== 'undefined'
-            ) {
-                reply.success = setScanCfg(
-                    http_request.query.sub[0],
-                    http_request.query.cfg[0]
-                );
-            } else {
-                reply.success = false;
-            }
-            break;
-
-        case 'vote':
-            if (typeof http_request.query.sub !== 'undefined' &&
-                typeof http_request.query.id !== 'undefined' &&
-                typeof http_request.query.up !== 'undefined' &&
-                !(user.security.restrictions&UFLAG_V)
-            ) {
-                reply.success = voteMessage(
-                    http_request.query.sub[0],
-                    http_request.query.id[0],
-                    http_request.query.up[0]
-                );
-            } else {
-                reply.success = false;
-            }
-            break;
-        
-        default:
-            break;
+    var handled = false;
+
+    // Authenticated calls
+    if (user.number > 0 && user.alias !== settings.guest) {
+
+        handled = true;
+
+        switch(http_request.query.call[0].toLowerCase()) {
+
+            case 'list-groups':
+                reply = listGroups();
+                break;
+            
+            case 'list-subs':
+                if (typeof http_request.query.group !== 'undefined') {
+                    reply = listSubs(http_request.query.group[0]);
+                }
+                break;
+            
+            case 'list-threads':
+                if (typeof http_request.query.sub !== 'undefined') {
+                    reply = listThreads(http_request.query.sub[0]);
+                }
+                break;
+            
+            case 'get-group-unread-count':
+                if (typeof http_request.query.group !== 'undefined') {
+                    http_request.query.group.forEach(
+                        function(group) {
+                            reply[group] = getGroupUnreadCount(group);
+                        }
+                    );
+                }
+                break;
+
+            case 'get-sub-unread-count':
+                if (typeof http_request.query.sub !== 'undefined') {
+                    http_request.query.sub.forEach(
+                        function(sub) {
+                            reply[sub] = getSubUnreadCount(sub);
+                        }
+                    );
+                }
+                break;
+
+            case 'get-mail-unread-count':
+                reply.count = getMailUnreadCount();
+                break;
+            
+            case 'get-mail-body':
+                if (typeof http_request.query.number !== 'undefined') {
+                    reply = getMailBody(http_request.query.number[0]);
+                }
+                break;
             
+            case 'get-signature':
+                reply.signature = getSignature();
+                break;
+            
+            case 'post-reply':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.body !== 'undefined' &&
+                    typeof http_request.query.pid !== 'undefined'
+                ) {
+                    reply.success = postReply(
+                        http_request.query.sub[0],
+                        http_request.query.body[0],
+                        Number(http_request.query.pid[0])
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+            
+            case 'post':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.to !== 'undefined' &&
+                    typeof http_request.query.subject !== 'undefined' &&
+                    typeof http_request.query.body !== 'undefined'
+                ) {
+                    reply.success = postNew(
+                        http_request.query.sub[0],
+                        http_request.query.to[0],
+                        http_request.query.subject[0],
+                        http_request.query.body[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+            
+            case 'delete-message':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.number !== 'undefined'
+                ) {
+                    reply.success = deleteMessage(
+                        http_request.query.sub[0],
+                        http_request.query.number[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'set-scan-cfg':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.cfg !== 'undefined'
+                ) {
+                    reply.success = setScanCfg(
+                        http_request.query.sub[0],
+                        http_request.query.cfg[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'vote':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined' &&
+                    typeof http_request.query.up !== 'undefined' &&
+                    !(user.security.restrictions&UFLAG_V)
+                ) {
+                    reply.success = voteMessage(
+                        http_request.query.sub[0],
+                        http_request.query.id[0],
+                        http_request.query.up[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'submit-poll-answers':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined' &&
+                    typeof http_request.query.answer !== 'undefined'
+                ) {
+                    reply.success = submitPollAnswers(
+                        http_request.query.sub[0],
+                        http_request.query.id[0],
+                        http_request.query.answer
+                    );
+                }
+                break;
+
+            default:
+                handled = false;
+                break;
+                
+        }
+
+    }
+
+    // Unauthenticated calls
+    if (!handled) {
+        switch(http_request.query.call[0].toLowerCase()) {
+            case 'get-thread-votes':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined'
+                ) {
+                    var id = parseInt(http_request.query.id[0]);
+                    if (!isNaN(id)) {
+                        reply = getVotesInThread(
+                            http_request.query.sub[0],
+                            id
+                        );
+                    }
+                }
+                break;
+            case 'get-sub-votes':
+                if (typeof http_request.query.sub !== 'undefined') {
+                    reply = getVotesInThreads(http_request.query.sub[0]);
+                }
+                break;
+            default:
+                break;
+        }
     }
 
 }
-- 
GitLab


From a088b172235252d816a4271e116ce8d59e5766f6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 18:56:12 -0500
Subject: [PATCH 118/752] Some CSS class name changes, other vote/poll related
 things.

---
 web/root/css/style.css | 62 ++++++++++++++++++++++++++++++++++------
 web/root/js/forum.js   | 64 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 116 insertions(+), 10 deletions(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 28e36cbfe3..5cc44f601a 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -73,21 +73,65 @@ input.dropdown {
 }
 
 /* Upvote button */
-.btn-uv {
+.btn-uv { }
 
+/* Downvote button */
+.btn-dv { }
+
+.upvote-bg {
+	background-color : #F0B27A;
 }
 
-/* Downvote button */
-.btn-dv {
+.downvote-bg {
+	background-color : #85C1E9;
+}
+
+.upvote-fg, .upvote:hover {
+	color : #F0B27A;
+}
+
+.downvote-fg, .downvote:hover {
+	color : #85C1E9;
+}
+
+.indicator {
+    background-color: transparent !important;
+    -webkit-animation: indicator-fade 3s ease 0s 1 alternate !important;
+}
+
+@-webkit-keyframes indicator-fade {
+    from { background-color: #82E0AA; }
+    to { background-color: transparent; }
+}
+
+-moz-animation: indicator-fade 3s ease 0s 1 alternate !important;
+}
+
+@-moz-keyframes indicator-fade {
+    from { background-color: #82E0AA; }
+    to { background-color: transparent; }
+}
+
+-ms-animation: indicator-fade 3s ease 0s 1 alternate !important;
+}
+
+@-ms-keyframes indicator-fade {
+    from { background-color: #82E0AA; }
+    to { background-color: transparent; }
+}
+
+-o-animation: indicator-fade 3s ease 0s 1 alternate !important;
+}
 
+@-o-keyframes indicator-fade {
+    from { background-color: #82E0AA;}
+    to { background-color: transparent; }
 }
 
-/* Upvote button (user has already upvoted) */
-.btn-uv-voted, .btn-uv-voted:hover {
-	color : orange;
+animation: indicator-fade 3s ease 0s 1 alternate !important;
 }
 
-/* Downvote button (user has already downvoted) */
-.btn-dv-voted, .btn-dv-voted:hover {
-	color : blue;
+@keyframes indicator-fade {
+    from { background-color: #82E0AA;}
+    to { background-color: transparent; }
 }
\ No newline at end of file
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 2f28e562da..c927e4d1b6 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -310,7 +310,7 @@ function vote(sub, id) {
 		function (data) {
 			if (!data.success) return;
 			$('#' + id[0] + '-' + id[1]).addClass(
-				id[0] === 'uv' ? 'btn-uv-voted' : 'btn-dv-voted'
+				id[0] === 'uv' ? 'upvote-fg' : 'downvote-fg'
 			);
 			$('#' + id[0] + '-' + id[1]).attr('disabled', true);
 			$('#' + id[0] + '-' + id[1]).blur();
@@ -323,4 +323,66 @@ function vote(sub, id) {
 function enableVoteButtonHandlers(sub) {
 	$('.btn-uv').click(function () { vote(sub, this.id); });
 	$('.btn-dv').click(function () { vote(sub, this.id); });
+}
+
+function getVotesInThread(sub, id) {
+	$.getJSON(
+		'./api/forum.ssjs?call=get-thread-votes&sub=' + sub + '&id=' + id,
+		function (data) {
+			Object.keys(data.m).forEach(
+				function (m) {
+					var uv = parseInt($('#uv-count-' + m).text());
+					var dv = parseInt($('#dv-count-' + m).text());
+					if (uv !== data.m[m].u) {
+						$('#uv-count-' + m).text(data.m[m].u);
+						$('#uv-' + m).addClass('indicator');
+					}
+					if (dv !== data.m[m].d) {
+						$('#dv-count-' + m).text(data.m[m].d);
+						$('#dv-' + m).addClass('indicator');
+					}
+					console.log(data.m[m]);
+					switch (data.m[m].v) {
+						case 1:
+							$('#uv-' + m).addClass('upvote-fg');
+							break;
+						case 2:
+							$('#dv-' + m).addClass('downvote-fg');
+							break;
+						default:
+							break;
+					}
+				}
+			);
+		}
+	);
+}
+
+function getVotesInThreads(sub) {
+	$.getJSON(
+		'./api/forum.ssjs?call=get-sub-votes&sub=' + sub,
+		function (data) {
+			Object.keys(data).forEach(
+				function (t) {
+					var uv = data[t].p.u + ' / ' + data[t].t.u;
+					var dv = data[t].p.d + ' / ' + data[t].t.d;
+					if (uv !== $('#uv-count-' + t).text()) {
+						$('#uv-count-' + t).text(uv);
+					}
+					if (dv !== $('#dv-count-' + t).text()) {
+						$('#dv-count-' + t).text(dv);
+					}
+				}
+			);
+		}
+	);
+}
+
+function submitPollAnswers(id) {
+	$('[name="poll-' + id + '"]:checked').each(
+		function () {
+			alert($(this).val());
+		}
+	);
+	// async ./api/forum.ssjs?call=submit-poll-answers&sub=x&id=x&answer=x&answer=x...
 }
\ No newline at end of file
-- 
GitLab


From 8b356a75f297ba80de244e98b17beb3d231e6d5b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 18:58:59 -0500
Subject: [PATCH 119/752] Moved all inline-HTML into a 'strings' object,
 staging for future customization possibilities.  Added a bunch of stuff to
 deal with poll-messages; should work, pending some bugfixes in Synchronet.

---
 web/root/pages/001-forum.ssjs | 658 ++++++++++++++++++----------------
 1 file changed, 353 insertions(+), 305 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 0160da14e5..74baabf4b7 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -5,19 +5,136 @@ load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'forum.js');
 
+var strings = {
+	script : {
+		open : '<script type="text/javascript">',
+		thread_navigation : 'threadNav();',
+		interval : 'setInterval(function () { %s }, %s);',
+		vote_buttons : 'enableVoteButtonHandlers("%s");',
+		vote_refresh_thread : 'getVotesInThread("%s", %s)',
+		vote_refresh_threads : 'getVotesInThreads("%s")',
+		get_group_unread : 'getGroupUnreadCount("%s")',
+		get_sub_unread : 'getSubUnreadCount("%s")',
+		close : '</script>'
+	},
+	notice_box : '<div id="noticebox" class="alert alert-warning">%s<script type="text/javascript">$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});</script>',
+	group_list : {
+		breadcrumb : '<ol class="breadcrumb"><li><a href="./?page=%s">Forum</a></li></ol>',
+		container : {
+			open : '<div id="forum-list-container" class="list-group">',
+			close : '</div>'
+		},
+		item : '<a href="./?page=%s&amp;group=%s" class="list-group-item striped"><h3><strong>%s</strong></h3><span title="Unread messages (other)" class="badge ignored" id="badge-ignored-%s"></span><span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-%s"></span><p>%s : %s sub-boards</p></a>'
+	},
+	sub_list : {
+		breadcrumb : '<ol class="breadcrumb"><li><a href="./?page=%s">Forum</a></li><li><a href="./?page=%s&amp;group=%s">%s</a></li></ol>',
+		container : {
+			open : '<div id="forum-list-container" class="list-group">',
+			close : '</div>'
+		},
+		item : '<a href="./?page=%s&amp;sub=%s" class="list-group-item striped%s"><h4><strong>%s</strong></h4><span title="Unread messages" class="badge %s" id="badge-%s"></span><p>%s</p></a>'
+	},
+	thread_list : {
+		breadcrumb : '<ol class="breadcrumb"><li><a href="./?page=%s">Forum</a></li><li><a href="./?page=%s&amp;group=%s">%s</a></li><li><a href="./?page=%s&amp;sub=%s">%s</a></li></ol>',
+		container : {
+			open : '<div id="forum-list-container" class="list-group">',
+			close : '</div>'
+		},
+		item : {
+			open : '<a href="./?page=%s&amp;sub=%s&amp;thread=%s" class="list-group-item striped"><div class="row">',
+			details : {
+				open : '<div class="col-sm-9">',
+				info : '<strong>%s</strong><p>By <strong>%s</strong> on %s</p><p>Latest reply by <strong>%s</strong> on %s</p>',
+				close : '</div>'
+			},
+			badges : {
+				open : '<div class="col-sm-3"><div class="pull-right">',
+				unread : '<span title="Unread messages" class="badge%s" id="badge-%s">%s</span>',
+				votes : {
+					up : '<span id="uv-%s" title="Upvotes - Parent / Thread Total" class="badge upvote-bg"><span class="glyphicon glyphicon-arrow-up"></span><span id="uv-count-%s">%s / %s</span></span>&nbsp;',
+					down : '<span id="dv-%s" title="Downvotes - Parent / Thread Total" class="badge downvote-bg"><span class="glyphicon glyphicon-arrow-down"></span><span id="dv-count-%s">%s / %s</span></span>',
+				},
+				close : '</div></div>'
+			},
+			close : '</div></a>'
+		},
+		controls : {
+			post : '<button class="btn btn-default icon" aria-label="Post a new message" title="Post a new message" onclick="addNew(\'%s\')"><span class="glyphicon glyphicon-pencil"></span></button>',
+			scan_new : '<button id="scan-cfg-new" class="btn %s icon" aria-label="Scan for new messages" title="Scan for new messages" onclick="setScanCfg(\'%s\', 1)"><span class="glyphicon glyphicon-ok-sign"></span></button>',
+			scan_you : '<button id="scan-cfg-youonly" class="btn %s icon" aria-label="Scan for new messages to you only" title="Scan for new messages to you only" onclick="setScanCfg(\'%s\', 2)"><span class="glyphicon glyphicon-user"></span></button>',
+			scan_off : '<button id="scan-cfg-off" class="btn %s icon" aria-label="Do not scan this sub" title="Do not scan this sub" onclick="setScanCfg(\'%s\', 0)"><span class="glyphicon glyphicon-ban-circle"></span></button>'
+		}
+	},
+	thread_view : {
+		breadcrumb : '<ol class="breadcrumb"><li><a href="./?page=%s">Forum</a></li><li><a href="./?page=%s&amp;group=%s">%s</a></li><li><a href="./?page=%s&amp;sub=%s">%s</a></li></ol>',
+		jump_unread : '<div id="jump-unread-container" style="margin-bottom:1em;" hidden><a class="btn btn-default" id="jump-unread" title="Jump to first unread message" href="#"><span class="glyphicon glyphicon-star"></span></a></div>',
+		set_unread : '$("#jump-unread").attr("href", "#%s");$("#jump-unread-container").attr("hidden", false);',
+		container : {
+			open : '<ul id="forum-list-container" class="list-group">',
+			close : '</ul>'
+		},
+		subject : '<h4><strong>%s</strong></h4>'
+	},
+	message : {
+		anchor : '<a id="%s"></a>',
+		open : '<li class="list-group-item striped" id="li-%s">',
+		header : {
+			open : '<div class="row">',
+			details : {
+				open : '<div class="col-sm-9">',
+				unread : '<span title="Unread" class="glyphicon glyphicon-star"></span>',
+				info : 'From <strong>%s</strong>%s to <strong>%s</strong> on %s',
+				close : '</div>'
+			},
+			voting : {
+				open : '<div class="col-sm-3"><div class="pull-right">',
+				poll : '<span class="badge">POLL</span>',
+				buttons : {
+					up : '<button id="uv-%s" class="btn-uv btn btn-default icon %s"><span title="Upvotes" class="glyphicon glyphicon-arrow-up"></span><span id="uv-count-%s" title="Upvotes">%s</span></button>',
+					down : '<button id="dv-%s" class="btn-dv btn btn-default icon %s"><span title="Downvotes" class="glyphicon glyphicon-arrow-down"></span><span id="dv-count-%s" title="Downvotes">%s</span></button>'
+				},
+				close : '</div></div>'
+			},
+			close : '</div>'
+		},
+		body : {
+			open : '<div class="message" id="message-%s">',
+			poll : {
+				comment : '<strong>%s</strong>',
+				answer : {
+					container : {
+						open : '<ul class="list-group">',
+						close : '</ul>'
+					},
+					open : '<li class="%s"><label><input type="%s" name="poll-%s" value="%s">%s</label> %s</li>',
+					closed : '<li%s>%s %s</li>'
+				},
+				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(%s)">Vote</button>',
+				closed : 'This poll has been closed.',
+				disallowed : 'You cannot vote on this poll.',
+				voted : 'You already voted on this poll.'
+			},
+			close : '</div>'
+		},
+		controls : {
+			oldest : '<a class="btn btn-default icon" title="Jump to oldest message" aria-label="Jump to oldest message" href="#%s"><span class="glyphicon glyphicon-fast-backward"></span></a>',
+			previous : '<a class="btn btn-default icon" title="Jump to previous message" aria-label="Jump to previous message" href="#%s" id="pm-%s"><span class="glyphicon glyphicon-step-backward"></span></a>',
+			link : '<a class="btn btn-default icon" title="Direct link to this message" aria-label="Direct link to this message" href="#%s"><span class="glyphicon glyphicon-link"></span></a>',
+			next : '<a class="btn btn-default icon" title="Jump to next message" aria-label="Jump to next message" href="#%s" id="nm-%s"><span class="glyphicon glyphicon-step-forward"></span></a>',
+			newest : '<a class="btn btn-default icon" title="Jump to newest message" aria-label="Jump to newest message" href="#%s"><span class="glyphicon glyphicon-fast-forward"></span></a>',
+			reply : '<button class="btn btn-default icon" title="Reply to this message" aria-label="Reply to this message" name="reply-%s" onclick="addReply(\'%s\', \'%s\')"><span class="glyphicon glyphicon-comment"></span></button>',
+			remove : '<button class="btn btn-default icon" title="Delete this message" aria-label="Delete this message" onclick="deleteMessage(\'%s\',\'%s\')"><span class="glyphicon glyphicon-trash"></span></button>'
+		},
+		close : '</li>'
+	}
+};
+
 writeln('<script type="text/javascript" src="./js/forum.js"></script>');
 
 if (typeof http_request.query.notice !== 'undefined') {
-	writeln(
-		'<div id="noticebox" class="alert alert-warning">' + 
-		http_request.query.notice[0] + '</div>' +
-		'<script type="text/javascript">' +
-		'$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});' +
-		'</script>'
-	);
+	writeln(format(strings.notice_box, http_request.query.notice[0]));
 }
 
-
 if (typeof http_request.query.sub !== 'undefined' &&
 	typeof msg_area.sub[http_request.query.sub[0]] !== 'undefined' &&
 	typeof http_request.query.thread !== 'undefined' &&
@@ -36,155 +153,149 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		var body = msgBase.get_msg_body(header.number);
 		if (body === null) return;
 
-		writeln(
-			'<a id="' + header.number + '"></a>' +
-			'<li class="list-group-item striped' +
-			'" id="li-' + header.number + '">'
-		);
+		writeln(format(strings.message.anchor, header.number));
+		writeln(format(strings.message.open, header.number));
 
 		// Show subject if first message
-		if (index === 0) {
-			writeln('<h4><strong>' + thread.subject + '</strong></h4>');
-		}
+		if (index === 0) writeln(format(strings.thread_view.subject, thread.subject));
 
 		// Header
-		writeln('<div class="row">');
+		writeln(strings.message.header.open);
 
-		writeln('<div class="col-sm-8">');
+		writeln(strings.message.header.details.open);
 		if (user.alias != settings.guest &&
 			header.number > msg_area.sub[http_request.query.sub[0]].scan_ptr
 		) {
-			writeln(
-				'<span title="Unread" class="glyphicon glyphicon-star"></span>'
-			);
+			writeln(strings.message.header.details.unread);
 			if (firstUnread === '') firstUnread += header.number;
 		}			
 		writeln(
-			'From <strong>' + header.from + "</strong>" +
-			(	typeof header.from_net_addr === 'undefined'
-				? ''
-				: ('@' +  header.from_net_addr)
-			) +
-			' to <strong>' + header.to + '</strong> on ' +
-			(new Date(header.when_written_time * 1000)).toLocaleString()
+			format(
+				strings.message.header.details.info,
+				header.from,
+				typeof header.from_net_addr === 'undefined' ? '' : '@' + header.from_net_addr,
+				header.to,
+				(new Date(header.when_written_time * 1000)).toLocaleString()
+			)
 		);
-		writeln('</div>');
+		writeln(strings.message.header.details.close);
 
-		if (typeof settings.vote_buttons === 'undefined' || settings.vote_buttons) {		
+		writeln(strings.message.header.voting.open);
+		if ((typeof settings.vote_buttons === 'undefined' || settings.vote_buttons) && !(header.attr&MSG_POLL)) {
 			writeln(
-				'<div class="col-sm-4">' +
-					'<div class="pull-right">' +
-					'<button id="uv-' + header.number + '" class="btn-uv btn btn-default icon"' + (user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '') + '>' +
-						'<span title="Upvotes" class="glyphicon glyphicon-arrow-up">' +
-						'</span>' +
-						'<span id="uv-count-' + header.number + '" title="Upvotes">' +
-						header.upvotes + '</span>' +
-					'</button>' +
-					'<button id="dv-' + header.number + '" class="btn-dv btn btn-default icon"' + (user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '') + '>' +
-						'<span title="Downvotes" class="glyphicon glyphicon-arrow-down">' +
-						'</span>' +
-						'<span id="dv-count-' + header.number + '" title="Downvotes">' +
-						header.downvotes + '</span>' +
-					'</button>' +
-					'</div>' +
-				'</div>'
+				format(
+					strings.message.header.voting.buttons.up,
+					header.number,
+					user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '',
+					header.number,
+					header.upvotes
+				)
 			);
+			writeln(
+				format(
+					strings.message.header.voting.buttons.down,
+					header.number,
+					user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '',
+					header.number,
+					header.downvotes
+				)
+			);
+		} else if (header.attr&MSG_POLL) {
+			writeln(strings.message.header.voting.poll);
 		}
+		writeln(strings.message.header.voting.close);
 		
-		writeln('</div>'); // message header
+		writeln(strings.message.header.close);
 
 		// Body
-		writeln(
-			'<div class="message" id="message-' + header.number + '">' +
-			formatMessage(body) + '</div>'
-		);
+		writeln(format(strings.message.body.open, header.number));
+		// If this is a poll message
+		if (header.attr&MSG_POLL) {
 
-		var prev = (
-			index === 0
-			? thread.messages[thread.__first].number
-			: thread.messages[keys[index - 1]].number
-		);
+			var pollData = getUserPollData(http_request.query.sub[0], header.number);
 
-		var next = (
-			key === thread.__last
-			? thread.messages[thread.__last].number
-			: thread.messages[keys[index + 1]].number
-		);
+			header.poll_comments.forEach(
+				function (e) {
+					writeln(format(strings.message.body.poll.comment, e.data));
+				}
+			);
+
+			writeln(strings.message.body.poll.answer.container.open);
+			// Poll is closed or user has voted
+			if (header.auxattr&POLL_CLOSED || pollData.answers > 0) {
+				header.poll.answers.forEach(
+					function (e, i) {
+						writeln(
+							format(
+								pollData.answers&(1<<i) ? 'class="upvote-bg"' : '',
+								strings.message.body.poll.answer.closed,
+								e.data,
+								pollData.show_results ? header.tally[i] : ''
+							)
+						);
+					}
+				);
+			// Poll is open and user hasn't voted
+			} else {
+				header.poll_answers.forEach(
+					function (e, i) {
+						writeln(
+							format(
+								strings.message.body.poll.answer.open,
+								header.votes < 2 ? 'radio' : 'checkbox',
+								header.votes < 2 ? 'radio' : 'checkbox',
+								header.number, i, e.data,
+								pollData.show_results ? header.tally[i] : ''
+							)
+						);
+					}
+				);
+			}
+			writeln(strings.message.body.poll.answer.container.close);
+
+			if (header.auxattr&POLL_CLOSED) {
+				writeln(strings.message.body.poll.closed);
+			} else if (pollData.answers > 0) {
+				writeln(strings.message.body.poll.voted);
+			} else if (user.alias == settings.guest || user.security.restrictions&UFLAG_V) {
+				writeln(strings.message.body.poll.disallowed);
+			} else {
+				writeln(format(strings.message.body.poll.button, header.number, header.number));
+			}
+
+		// This is a normal message
+		} else {
+			writeln(formatMessage(body));
+		}
+		writeln(strings.message.body.close);
+
+		var prev = index === 0 ? thread.messages[thread.__first].number : thread.messages[keys[index - 1]].number;
+		var next = key === thread.__last ? thread.messages[thread.__last].number : thread.messages[keys[index + 1]].number;
 
 		// Standard controls
-		writeln(
-			'<a class="btn btn-default icon" title="Jump to oldest message" ' +
-			'aria-label="Jump to oldest message" href="#' +
-			thread.messages[thread.__first].number + '">' +
-			'<span class="glyphicon glyphicon-fast-backward"></span></a>' +
-
-			'<a class="btn btn-default icon" title="Jump to previous message" '+
-			'aria-label="Jump to previous message" href="#' + prev + '" ' +
-			'id="pm-' + header.number + '">' +
-			'<span class="glyphicon glyphicon-step-backward"></span></a>' +
-
-			'<a class="btn btn-default icon" ' +
-			'title="Direct link to this message" ' +
-			'aria-label="Direct link to this message" ' +
-			'href="#' + header.number + '">' +
-			'<span class="glyphicon glyphicon-link"></span></a>' +
-
-			'<a class="btn btn-default icon" title="Jump to next message" ' +
-			'aria-label="Jump to next message" href="#' + next + '" ' +
-			'id="nm-' + header.number + '">' +
-			'<span class="glyphicon glyphicon-step-forward"></span></a>' +
-			
-			'<a class="btn btn-default icon" ' +
-			'aria-label="Jump to newest message" ' +
-			'title="Jump to newest message" href="#' + 
-			thread.messages[thread.__last].number + 
-			'"><span class="glyphicon glyphicon-fast-forward"></span></a>'
-		);
+		writeln(format(strings.message.controls.oldest, thread.messages[thread.__first].number));
+		writeln(format(strings.message.controls.previous, prev, header.number));
+		writeln(format(strings.message.controls.link, header.number));
+		writeln(format(strings.message.controls.next, next, header.number));
+		writeln(format(strings.message.controls.newest, thread.messages[thread.__last].number));
 
 		// User can post
-		if (user.alias !== settings.guest &&
-			msg_area.sub[msgBase.cfg.code].can_post
-		) {
-			writeln(
-				'<button class="btn btn-default icon" ' +
-				'aria-label="Reply to this message" ' +
-				'title="Reply to this message" ' +
-				'name="reply-' + header.number + '" ' +
-				'onclick="addReply(\'' +
-					msgBase.cfg.code + '\',' + header.number +
-				')">' +
-				'<span class="glyphicon glyphicon-comment"></span>' +
-				'</button>'
-			);
+		if (user.alias !== settings.guest && msg_area.sub[msgBase.cfg.code].can_post) {
+			writeln(format(strings.message.controls.reply, header.number, msgBase.cfg.code, header.number));
 		}
 
 		// User is operator
-		if (user.alias != settings.guest &&
-			msg_area.sub[msgBase.cfg.code].is_operator
-		) {
-			writeln(
-				'<button class="btn btn-default icon" ' +
-				'aria-label="Delete this message" title="Delete this message" '+
-				'onclick="deleteMessage(\'' +
-					msgBase.cfg.code + '\', ' + header.number +
-				')" ' +
-				'href="#">' +
-				'<span class="glyphicon glyphicon-trash"></span>' +
-				'</button>'
-			);
+		if (user.alias != settings.guest && msg_area.sub[msgBase.cfg.code].is_operator) {
+			writeln(format(strings.message.controls.remove, msgBase.cfg.code, header.number));
 		}
 
-		writeln('</li>');
+		writeln(strings.message.close);
 
 	}
 
 	writeln(
 		format(
-			'<ol class="breadcrumb">' +
-			'<li><a href="./?page=%s">Forum</a></li>' +
-			'<li><a href="./?page=%s&amp;group=%s">%s</a></li>' +
-			'<li><a href="./?page=%s&amp;sub=%s">%s</a></li>' +
-			'</ol>',
+			strings.thread_view.breadcrumb,
 			http_request.query.page[0],
 			http_request.query.page[0],
 			msg_area.sub[http_request.query.sub[0]].grp_index,
@@ -194,13 +305,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			msg_area.sub[http_request.query.sub[0]].name
 		)
 	);
-	writeln(
-		'<div id="jump-unread-container" style="margin-bottom:1em;" hidden>' +
-		'<a class="btn btn-default" id="jump-unread" ' +
-		'title="Jump to first unread message" href="#">' +
-		'<span class="glyphicon glyphicon-star"></span>' +
-		'</a></div>'
-	);
+	writeln(strings.thread_view.jump_unread);
 
 	try {
 		msgBase.open();
@@ -213,26 +318,17 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		var keys = Object.keys(thread.messages);
 		thread.__first = keys[0];
 		thread.__last = keys[keys.length - 1];
-		writeln('<ul id="forum-list-container" class="list-group">');
-		keys.forEach(
-			function (key, index) {
-				writeMessage(thread, keys, key, index);
-			}
-		);
-		writeln('</ul>');
+		writeln(strings.thread_view.container.open);
+		keys.forEach(function (key, index) { writeMessage(thread, keys, key, index); });
+		writeln(strings.thread_view.container.close);
 		msgBase.close();
 		if (keys.length > 1 && firstUnread !== '') {
-			writeln(
-				'<script type="text/javascript">' +
-				'$("#jump-unread").attr("href", "#' + firstUnread + '");' +
-				'$("#jump-unread-container").attr("hidden", false);' +
-				'</script>'
-			);
+			writeln(strings.script.open);
+			writeln(format(strings.thread_view.set_unread, firstUnread));
+			writeln(strings.script.close);
 		}
 		// Update scan pointer
-		if (thread.messages[thread.__last].number >
-			msg_area.sub[http_request.query.sub[0]].scan_ptr
-		) {
+		if (thread.messages[thread.__last].number > msg_area.sub[http_request.query.sub[0]].scan_ptr) {
 			msg_area.sub[http_request.query.sub[0]].scan_ptr =
 			thread.messages[thread.__last].number;
 		}
@@ -240,14 +336,28 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		log(LOG_WARNING, err);
 	}
 
-	writeln('<script type="text/javascript">');
-	if (settings.keyboard_navigation) {
-		writeln('threadNav();');
-	}
+	writeln(strings.script.open);
+	if (settings.keyboard_navigation) writeln(strings.script.thread_navigation);
 	if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
-		writeln('enableVoteButtonHandlers("'+http_request.query.sub[0]+'");');
+		writeln(format(strings.script.vote_buttons, http_request.query.sub[0]));
 	}
-	writeln('</script>');
+	writeln(
+		format(
+			strings.script.vote_refresh_thread,
+			http_request.query.sub[0], thread.__first
+		)
+	);
+	writeln(
+		format(
+			strings.script.interval,
+			format(
+				strings.script.vote_refresh_thread,
+				http_request.query.sub[0], thread.__first
+			),
+			settings.refresh_interval || 60000
+		)
+	);
+	writeln(strings.script.close);
 
 } else if (
 	typeof http_request.query.sub !== 'undefined' &&
@@ -257,6 +367,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	// Thread list
 	function writeThread(thread) {
+
 		if (user.number > 0 && user.alias != settings.guest) {
 			var unread = getUnreadInThread(http_request.query.sub[0], thread);
 		} else {
@@ -267,73 +378,64 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		var last = keys[keys.length - 1];
 		writeln(
 			format(
-				'<a href="./?page=%s&amp;sub=%s&amp;thread=%s" ' +
-				'class="list-group-item striped">',
+				strings.thread_list.item.open,
 				http_request.query.page[0],
 				http_request.query.sub[0],
 				thread.id
 			)
 		);
-		writeln('<div class="row"><div class="col-sm-8">');
+
+		writeln(strings.thread_list.item.details.open);
 		writeln(
 			format(
-				'<strong>%s</strong>' +
-				'<p>By <strong>%s</strong> on %s</p>' +
-				'<p>Latest reply by <strong>%s</strong> on %s</p>',
+				strings.thread_list.item.details.info,
 				thread.subject,
 				thread.messages[first].from,
-				(new Date(
-						thread.messages[first].when_written_time * 1000
-					)
-				).toLocaleString(),
+				(new Date(thread.messages[first].when_written_time * 1000)).toLocaleString(),
 				thread.messages[last].from,
-				(new Date(
-					thread.messages[last].when_written_time * 1000
-				)).toLocaleString()
+				(new Date(thread.messages[last].when_written_time * 1000)).toLocaleString()
 			)
 		);
-		writeln('</div>');
-		if (settings.vote_buttons) {
+		writeln(strings.thread_list.item.details.close);
+
+		writeln(strings.thread_list.item.badges.open);
+		if (settings.vote_buttons && (thread.votes.up > 0 || thread.votes.down > 0)) {
+			writeln(
+				format(
+					strings.thread_list.item.badges.votes.up,
+					thread.messages[first].number,
+					thread.messages[first].number,
+					thread.messages[first].upvotes,
+					thread.votes.up
+				)
+			);
 			writeln(
-				'<div class="col-sm-3">' +
-				'<div class="pull-right">' +
-				'<div title="Upvotes - Parent (Thread Total)" class="btn icon">' +
-				'<span class="glyphicon glyphicon-arrow-up">' +
-				'</span>' +
-				thread.messages[first].upvotes + ' (' + thread.votes.up + ')' +
-				'</div>' +
-				'<div title="Downvotes - Parent (Thread Total)" class="btn icon">' +
-				'<span class="glyphicon glyphicon-arrow-down">' +
-				'</span>' +
-				thread.messages[first].downvotes + ' (' + thread.votes.down + ')' +
-				'</div>' +
-				'</div>' +
-				'</div>'
+				format(
+					strings.thread_list.item.badges.votes.down,
+					thread.messages[first].number,
+					thread.messages[first].number,
+					thread.messages[first].downvotes,
+					thread.votes.down
+				)
 			);
 		}
 		writeln(
 			format(
-				'<div class="col-sm-1">' +
-				'<div class="btn icon">' +
-				'<span title="Unread messages" class="badge%s" id="badge-%s">' +
-				'%s</span></div></div>',
-				(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW
-					? ' scanned' : ''
-				),
+				strings.thread_list.item.badges.unread,
+				msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW ? ' scanned' : '',
 				thread.messages[first].number,
 				(unread == 0 ? '' : unread)
 			)
 		);
-		writeln('</div></a>');
+		writeln(strings.thread_list.item.badges.close);
+
+		writeln(strings.thread_list.item.close);
+
 	}
 
 	writeln(
 		format(
-			'<ol class="breadcrumb">' +
-			'<li><a href="./?page=%s">Forum</a></li>' +
-			'<li><a href="./?page=%s&amp;group=%s">%s</a></li>' +
-			'<li><a href="./?page=%s&amp;sub=%s">%s</a></li>' +
-			'</ol>',
+			strings.thread_list.breadcrumb,
 			http_request.query.page[0],
 			http_request.query.page[0],
 			msg_area.sub[http_request.query.sub[0]].grp_index,
@@ -344,74 +446,53 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		)
 	);
 
-	if (user.alias !== settings.guest &&
-		msg_area.sub[http_request.query.sub[0]].can_post
-	) {
-		writeln(
-			'<button class="btn btn-default icon" ' +
-			'aria-label="Post a new message" title="Post a new message" ' +
-			'onclick="addNew(\'' + http_request.query.sub[0] + '\')">' +
-			'<span class="glyphicon glyphicon-pencil"></span>' +
-			'</button>'
-		);
+	if (user.alias !== settings.guest && msg_area.sub[http_request.query.sub[0]].can_post) {
+		writeln(format(strings.thread_list.controls.post, http_request.query.sub[0]));
 	}
 
 	if (user.alias !== settings.guest) {
 		writeln(
-			'<button id="scan-cfg-new" class="btn ' +
-			(	!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) &&
-				(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW)
-				? 'btn-primary'
-				: 'btn-default'
-			) +
-			' icon" aria-label="Scan for new messages" ' +
-			'title="Scan for new messages" ' +
-			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',1)"' +
-			'>' +
-			'<span class="glyphicon glyphicon-ok-sign"></span>' +
-			'</button>'
+			format(
+				strings.thread_list.controls.scan_new,
+				!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) && msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW ? 'btn-primary' : 'btn-default',
+				http_request.query.sub[0]
+			)
 		);
 		writeln(
-			'<button id="scan-cfg-youonly" class="btn ' +
-			(	msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY
-				? 'btn-primary'
-				: 'btn-default'
-			) +
-			' icon" aria-label="Scan for new messages to you only" ' +
-			'title="Scan for new messages to you only" ' +
-			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',2)"' +
-			'>' +
-			'<span class="glyphicon glyphicon-user"></span>' +
-			'</button>'
+			format(
+				strings.thread_list.controls.scan_you,
+				msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY ? 'btn-primary' : 'btn-default',
+				http_request.query.sub[0]
+			)
 		);
 		writeln(
-			'<button id="scan-cfg-off" class="btn ' +
-			(	!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) &&
-				!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY)
-				? 'btn-primary'
-				: 'btn-default'
-			) +
-			' icon" aria-label="Do not scan this sub" ' +
-			'title="Do not scan this sub" ' +
-			'onclick="setScanCfg(\'' + http_request.query.sub[0] + '\',0)"' +
-			'>' +
-			'<span class="glyphicon glyphicon-ban-circle"></span>' +
-			'</button>'
+			format(
+				strings.thread_list.controls.scan_off,
+				!(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_NEW) && !(msg_area.sub[http_request.query.sub[0]].scan_cfg&SCAN_CFG_YONLY) ? 'btn-primary' : 'btn-default',
+				http_request.query.sub[0]
+			)
 		);
 	}
 
 	try {
-		var threads = getMessageThreads(
-			http_request.query.sub[0],
-			settings.max_messages
-		);
-		writeln('<div id="forum-list-container" class="list-group">');
+		var threads = getMessageThreads(http_request.query.sub[0], settings.max_messages);
+		writeln(strings.thread_list.container.open);
 		threads.order.forEach(function(t){writeThread(threads.thread[t]);});
-		writeln('</div>');
+		writeln(strings.thread_list.container.close);
 	} catch (err) {
 		log(LOG_WARNING, err);
 	}
 
+	writeln(strings.script.open);
+	writeln(
+		format(
+			strings.script.interval,
+			format(strings.script.vote_refresh_threads, http_request.query.sub[0]),
+			settings.refresh_interval || 60000
+		)
+	);
+	writeln(strings.script.close);
+
 } else if (
 	typeof http_request.query.group !== 'undefined' &&
 	typeof msg_area.grp_list[http_request.query.group[0]] !== 'undefined'
@@ -421,24 +502,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	function writeSub(sub) {
 		writeln(
 			format(
-				'<a href="./?page=%s&amp;sub=%s" ' +
-				'class="list-group-item striped%s">' +
-				'<h4><strong>%s</strong></h4>' +
-				'<span title="Unread messages" class="badge %s" id="badge-%s">'+
-				'</span>' +
-				'<p>%s</p>' +
-				'</a>',
+				strings.sub_list.item,
 				http_request.query.page[0],
 				sub.code,
-				(	sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY
-					? ' scanned'
-					: ''
-				),
+				sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY ? ' scanned' : '',
 				sub.name,
-				(	sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY
-					? 'scanned'
-					: 'total'
-				),
+				sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : 'total',
 				sub.code,
 				sub.description
 			)
@@ -446,26 +515,24 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	}
 
 	function writeApiCall(subs) {
-		writeln('<script type="text/javascript">');
+		writeln(strings.script.open);
 		var codes = [];
 		subs.forEach(function (sub) { codes.push(sub.code); });
 		codes = codes.join('&sub=');
-		writeln('getSubUnreadCount("' + codes + '");');
+		writeln(format(strings.script.get_sub_unread, codes));
 		writeln(
-			'setInterval(' +
-				'function(){getSubUnreadCount("' + codes + '");},' +
-				'60000' +
-			');'
+			format(
+				strings.script.interval,
+				format(strings.script.get_sub_unread, codes),
+				settings.refresh_interval || 60000
+			)
 		);
-		writeln('</script>');
+		writeln(strings.script.close);
 	}
 
 	writeln(
 		format(
-			'<ol class="breadcrumb">' +
-			'<li><a href="./?page=%s">Forum</a></li>' +
-			'<li><a href="./?page=%s&amp;group=%s">%s</a></li>' +
-			'</ol>',
+			strings.sub_list.breadcrumb,
 			http_request.query.page[0],
 			http_request.query.page[0],
 			http_request.query.group[0],
@@ -475,12 +542,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	try {
 		var subs = listSubs(http_request.query.group[0]);
-		writeln('<div id="forum-list-container" class="list-group">');
+		writeln(strings.sub_list.container.open);
 		subs.forEach(writeSub);
-		writeln('</div>');
-		if (user.number > 0 && user.alias !== settings.guest) {
-			writeApiCall(subs);
-		}
+		writeln(strings.sub_list.container.close);
+		if (user.number > 0 && user.alias !== settings.guest) writeApiCall(subs);
 	} catch (err) {
 		log(LOG_WARNING, err);
 	}
@@ -491,17 +556,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	function writeGroup(group) {
 		writeln(
 			format(
-				'<a href="./?page=%s&amp;group=%s" ' +
-				'class="list-group-item striped">' +
-				'<h3><strong>%s</strong></h3>' +
-				'<span title="Unread messages (other)" ' +
-				'class="badge ignored" id="badge-ignored-%s">' +
-				'</span>' +
-				'<span title="Unread messages (scanned subs)" ' +
-				'class="badge scanned" id="badge-scanned-%s">' +
-				'</span>' +
-				'<p>%s : %s sub-boards</p>' +
-				'</a>',
+				strings.group_list.item,
 				http_request.query.page[0],
 				group.index,
 				group.name,
@@ -514,36 +569,29 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	}
 
 	function writeApiCall(groups) {
-		writeln('<script type="text/javascript">');
+		writeln(strings.script.open);
 		var indexes = [];
 		groups.forEach(function(group) { indexes.push(group.index); });
 		indexes = indexes.join('&group=');
-		writeln('getGroupUnreadCount("' + indexes + '");');
+		writeln(format(strings.script.get_group_unread, indexes));
 		writeln(
-			'setInterval(' +
-				'function(){getGroupUnreadCount("' + indexes + '");},' +
-				'60000' +
-			');'
+			format(
+				strings.script.interval,
+				format(strings.script.get_group_unread, indexes),
+				settings.refresh_interval || 60000
+			)
 		);
-		writeln('</script>');
+		writeln(strings.script.close);
 	}
 	
-	writeln(
-		'<ol class="breadcrumb">' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '">Forum</a>' +
-		'</li>' +
-		'</ol>'
-	);
+	writeln(format(strings.group_list.breadcrumb, http_request.query.page[0]));
 
 	try {
 		var groups = listGroups();
-		writeln('<div id="forum-list-container" class="list-group">');
+		writeln(strings.group_list.container.open);
 		groups.forEach(writeGroup);
-		writeln('</div>');
-		if (user.number > 0 && user.alias !== settings.guest) {
-			writeApiCall(groups);
-		}
+		writeln(strings.group_list.container.close);
+		if (user.number > 0 && user.alias !== settings.guest) writeApiCall(groups);
 	} catch (err) {
 		log(LOG_WARNING, err);
 	}
-- 
GitLab


From 0e8325d0e695861af5e9da8ef81a2ec2e5746d2b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 18:59:29 -0500
Subject: [PATCH 120/752] Respect the new refresh_interval setting, if it
 exists.

---
 web/root/sidebar/001-nodelist.xjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/sidebar/001-nodelist.xjs b/web/root/sidebar/001-nodelist.xjs
index 2126946bab..a798e2b0ac 100644
--- a/web/root/sidebar/001-nodelist.xjs
+++ b/web/root/sidebar/001-nodelist.xjs
@@ -48,5 +48,5 @@ var nodeList = function() {
 	);
 }
 nodeList();
-setInterval(nodeList, 30000);
-</script>
\ No newline at end of file
+setInterval(nodeList, <?xjs write(settings.refresh_interval || 60000); ?>);
+</script>
-- 
GitLab


From 4066be882191f3e18392963881239ce1d0e450f7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 19:00:56 -0500
Subject: [PATCH 121/752] Added refresh_interval setting to modopts.ini section
 example.

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 52d89136dd..8bc72079c9 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,8 @@ A web interface for Synchronet BBS
 	keyboard_navigation = false
 	; Display upvote/downvote buttons in message threads (3.17)
 	vote_buttons = true
+	; Refresh nodelist, vote counts, etc. this often (in milliseconds)
+	refresh_interval = 60000
 ```
 - Add the following section to your *ctrl/services.ini* file:
 ```ini
-- 
GitLab


From 61d2c1f2ab1726d0e9e94f02a7ce995319897256 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 23:06:15 -0500
Subject: [PATCH 122/752] Changed recommended DOVE-Net sub & link to Synchronet
 Discussion

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 8bc72079c9..f027840f0d 100644
--- a/README.md
+++ b/README.md
@@ -127,7 +127,7 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 
 ####On DOVE-Net
 
-- Post a message to *echicken* in [Synchronet Sysops](https://bbs.electronicchicken.com/?page=001-forum.ssjs&sub=sync_sys).  I read this sub regularly and will respond to you there.
+- Post a message to *echicken* in [Synchronet Discussion](https://bbs.electronicchicken.com/?page=001-forum.ssjs&sub=sync).  I read this sub regularly and will respond to you there.
 
 ####On IRC
 
-- 
GitLab


From c69e376ce2d7daf9bb1b115293ad643ad9d882fa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 23:10:39 -0500
Subject: [PATCH 123/752] Don't allow voting if user has REST V

---
 web/lib/forum.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 9cc48856a7..ca5fb36328 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -493,6 +493,7 @@ function voteMessage(sub, number, up) {
     if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail') {
         return false;
     }
+    if (user.security.restrictions&UFLAG_V) return false;
     number = parseInt(number);
     if (isNaN(number)) return false;
     up = parseInt(up);
-- 
GitLab


From 2d6ff093819d213efbeb38018a8e943cf6434982 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Nov 2016 23:33:35 -0500
Subject: [PATCH 124/752] Fleshed out submitPollAnswers somewhat, still not
 usable until I know how to supply the answers themselves.

---
 web/lib/forum.js | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index ca5fb36328..158edce7e2 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -494,6 +494,7 @@ function voteMessage(sub, number, up) {
         return false;
     }
     if (user.security.restrictions&UFLAG_V) return false;
+    if (msg_area.sub[sub].settings&SUB_NOVOTING) return false;
     number = parseInt(number);
     if (isNaN(number)) return false;
     up = parseInt(up);
@@ -501,9 +502,12 @@ function voteMessage(sub, number, up) {
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return false;
     var header = msgBase.get_msg_header(number);
-    if (header === null) return false;
+    if (header === null) {
+        msgBase.close();
+        return false;
+    }
     var vh = {
-        'from' : user.alias,
+        'from' : msgBase.cfg.settings&SUB_NAME ? user.name : user.alias,
         'from_ext' : user.number,
         'from_net_type' : NET_NONE,
         'thread_back' : header.number,
@@ -515,10 +519,31 @@ function voteMessage(sub, number, up) {
 }
 
 function submitPollAnswers(sub, number, answers) {
-    // verify that user can vote
-    // verify that the poll is open
-    // msgbase.vote_msg
-    return true;
+    if (typeof msg_area.sub[sub] === 'undefined') return false;
+    if (msg_area.sub[sub].settings&SUB_NOVOTING) return false;
+    if (user.security.restrictions&UFLAG_V) return false;
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return false;
+    var ret = false;
+    var header = msgBase.get_msg_header(number);
+    if (header !== null && header.attr&MSG_POLL && !(header.auxattr&POLL_CLOSED)) {
+        var uv = msgBase.how_user_voted(
+            number, msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
+        );
+        if (uv === 0) {
+            ret = msgBase.vote_msg(
+                {   'from' : msgBase.cfg.settings&SUB_NAME ? user.name : user.alias,
+                    'from_ext' : user.number,
+                    'from_net_type' : NET_NONE,
+                    'thread_back' : number,
+                    'attr' : MSG_VOTE
+                    // supply the answers here ... somehow
+                }
+            );
+        }
+    }
+    msgBase.close();
+    return ret;
 }
 
 // Deuce's URL-ifier
-- 
GitLab


From df08aed784fd9891f17283e95c58cda73e82f49b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 00:01:06 -0500
Subject: [PATCH 125/752] Disable vote & poll buttons if sub has SUB_NOVOTING
 setting.

---
 web/root/pages/001-forum.ssjs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 74baabf4b7..0713544d45 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -186,7 +186,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				format(
 					strings.message.header.voting.buttons.up,
 					header.number,
-					user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '',
+					user.alias == settings.guest || user.security.restrictions&UFLAG_V || msgBase.cfg.settings&SUB_NOVOTING ? 'disabled' : '',
 					header.number,
 					header.upvotes
 				)
@@ -195,7 +195,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				format(
 					strings.message.header.voting.buttons.down,
 					header.number,
-					user.alias == settings.guest || user.security.restrictions&UFLAG_V ? 'disabled' : '',
+					user.alias == settings.guest || user.security.restrictions&UFLAG_V || msgBase.cfg.settings&SUB_NOVOTING ? 'disabled' : '',
 					header.number,
 					header.downvotes
 				)
@@ -257,7 +257,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				writeln(strings.message.body.poll.closed);
 			} else if (pollData.answers > 0) {
 				writeln(strings.message.body.poll.voted);
-			} else if (user.alias == settings.guest || user.security.restrictions&UFLAG_V) {
+			} else if (user.alias == settings.guest || user.security.restrictions&UFLAG_V || msgBase.cfg.settings&SUB_NOVOTING) {
 				writeln(strings.message.body.poll.disallowed);
 			} else {
 				writeln(format(strings.message.body.poll.button, header.number, header.number));
-- 
GitLab


From 114e393bb1469af3860fa2e0a8a842065642e755 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 01:09:42 -0500
Subject: [PATCH 126/752] Make header.tally if it doesn't already exist for a
 poll message.

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 158edce7e2..9b9ae874f9 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -775,7 +775,7 @@ var getMessageThreads = function(sub, max) {
                 }
             );
             threads.thread[thread_id].messages[header.number].votes = header.votes;
-            threads.thread[thread_id].messages[header.number].tally = header.tally;
+            threads.thread[thread_id].messages[header.number].tally = header.tally || [];
         } else {
             threads.thread[thread_id].votes.up += (header.upvotes || 0);
             threads.thread[thread_id].votes.down += (header.downvotes || 0);
-- 
GitLab


From 51df22d737b26e2940ac48c23fe3b7e74d862d47 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 01:11:22 -0500
Subject: [PATCH 127/752] Added (untested, ugly) pollControl function &
 callbacks. Enforce x number of answers on a multi-answer (checkbox) poll;
 will also be enforced on the back end.  Can't test as I don't know how to
 post such a poll yet.

---
 web/root/js/forum.js | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index c927e4d1b6..82964b37b9 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -379,10 +379,30 @@ function getVotesInThreads(sub) {
 }
 
 function submitPollAnswers(id) {
-	$('[name="poll-' + id + '"]:checked').each(
+	$('input[name="poll-' + id + '"]:checked').each(
 		function () {
 			alert($(this).val());
 		}
 	);
 	// async ./api/forum.ssjs?call=submit-poll-answers&sub=x&id=x&answer=x&answer=x...
+}
+
+function pollControl(id, count) {
+	$('input[name="poll-' + id + '"]').each(
+		function () {
+			$(this).change(
+				function () {
+					if ($('input[name="poll-' + id + '"]:checked').length >= count) {
+						$('input[name="poll-' + id + '"]:not(:checked)').each(
+							function () { $(this).attr('disabled', true); }
+						);
+					} else {
+						$('input[name="poll-' + id + '"]:not(:checked)').each(
+							function () { $(this).attr('disabled', false); }
+						);
+					}
+				}
+			);
+		}
+	);
 }
\ No newline at end of file
-- 
GitLab


From b14246d71cd1e24a6831adfd59a6f054c67694ee Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 01:12:22 -0500
Subject: [PATCH 128/752] Call pollControl(id, count) after a poll message has
 been written out.  Deal with missing/empty header.tally.

---
 web/root/pages/001-forum.ssjs | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 0713544d45..e482d26d7c 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -15,6 +15,7 @@ var strings = {
 		vote_refresh_threads : 'getVotesInThreads("%s")',
 		get_group_unread : 'getGroupUnreadCount("%s")',
 		get_sub_unread : 'getSubUnreadCount("%s")',
+		poll_control : 'pollControl("%s", %s)',
 		close : '</script>'
 	},
 	notice_box : '<div id="noticebox" class="alert alert-warning">%s<script type="text/javascript">$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});</script>',
@@ -223,14 +224,14 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			writeln(strings.message.body.poll.answer.container.open);
 			// Poll is closed or user has voted
 			if (header.auxattr&POLL_CLOSED || pollData.answers > 0) {
-				header.poll.answers.forEach(
+				header.poll_answers.forEach(
 					function (e, i) {
 						writeln(
 							format(
 								pollData.answers&(1<<i) ? 'class="upvote-bg"' : '',
 								strings.message.body.poll.answer.closed,
 								e.data,
-								pollData.show_results ? header.tally[i] : ''
+								pollData.show_results ? header.tally[i] || 0 : ''
 							)
 						);
 					}
@@ -245,7 +246,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 								header.votes < 2 ? 'radio' : 'checkbox',
 								header.votes < 2 ? 'radio' : 'checkbox',
 								header.number, i, e.data,
-								pollData.show_results ? header.tally[i] : ''
+								pollData.show_results ? header.tally[i] || 0 : ''
 							)
 						);
 					}
@@ -263,6 +264,12 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				writeln(format(strings.message.body.poll.button, header.number, header.number));
 			}
 
+			if (header.votes > 1) {
+				writeln(strings.script.open);
+				writeln(format(strings.script.poll_control, header.number, header.votes));
+				writeln(strings.script.close);
+			}
+
 		// This is a normal message
 		} else {
 			writeln(formatMessage(body));
-- 
GitLab


From 95d9fdb73292e9badad325e6293f4d9f06bf1818 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 01:26:48 -0500
Subject: [PATCH 129/752] submitPollAnswers - provide a 'votes' header property
 (answers bitfield)

---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 9b9ae874f9..9cad6886b3 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -536,8 +536,8 @@ function submitPollAnswers(sub, number, answers) {
                     'from_ext' : user.number,
                     'from_net_type' : NET_NONE,
                     'thread_back' : number,
-                    'attr' : MSG_VOTE
-                    // supply the answers here ... somehow
+                    'attr' : MSG_VOTE,
+                    'votes' : answers
                 }
             );
         }
-- 
GitLab


From cfee0ebea0b304ff6c340c57620ee0412f9a5bb7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 02:00:43 -0500
Subject: [PATCH 130/752] Fix out-of-order format() args when generating <ul>
 for a closed or already-answered poll. Style answered items properly (using
 'upvoted' background colour for these list items.)

---
 web/root/pages/001-forum.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index e482d26d7c..8385d2ee5e 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -108,7 +108,7 @@ var strings = {
 						close : '</ul>'
 					},
 					open : '<li class="%s"><label><input type="%s" name="poll-%s" value="%s">%s</label> %s</li>',
-					closed : '<li%s>%s %s</li>'
+					closed : '<li class="checkbox%s">%s %s</li>'
 				},
 				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(%s)">Vote</button>',
 				closed : 'This poll has been closed.',
@@ -228,8 +228,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 					function (e, i) {
 						writeln(
 							format(
-								pollData.answers&(1<<i) ? 'class="upvote-bg"' : '',
 								strings.message.body.poll.answer.closed,
+								pollData.answers&(1<<i) ? ' upvote-bg' : '',
 								e.data,
 								pollData.show_results ? header.tally[i] || 0 : ''
 							)
-- 
GitLab


From 21e7a27b4282edad776954dc239a1bd000660fb0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 02:03:47 -0500
Subject: [PATCH 131/752] After styling an already upvoted/downvoted button,
 disable it.

---
 web/root/js/forum.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 82964b37b9..31553bd29a 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -341,13 +341,14 @@ function getVotesInThread(sub, id) {
 						$('#dv-count-' + m).text(data.m[m].d);
 						$('#dv-' + m).addClass('indicator');
 					}
-					console.log(data.m[m]);
 					switch (data.m[m].v) {
 						case 1:
 							$('#uv-' + m).addClass('upvote-fg');
+							$('#uv-' + m).attr('disabled', true);							
 							break;
 						case 2:
 							$('#dv-' + m).addClass('downvote-fg');
+							$('#dv-' + m).attr('disabled', true);							
 							break;
 						default:
 							break;
-- 
GitLab


From 0bd19b49fc2c5260b1a34bffce076217e45ffb0b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 02:05:41 -0500
Subject: [PATCH 132/752] Actually ... disable both of them.

---
 web/root/js/forum.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 31553bd29a..d85d751260 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -345,9 +345,11 @@ function getVotesInThread(sub, id) {
 						case 1:
 							$('#uv-' + m).addClass('upvote-fg');
 							$('#uv-' + m).attr('disabled', true);							
+							$('#dv-' + m).attr('disabled', true);							
 							break;
 						case 2:
 							$('#dv-' + m).addClass('downvote-fg');
+							$('#uv-' + m).attr('disabled', true);							
 							$('#dv-' + m).attr('disabled', true);							
 							break;
 						default:
-- 
GitLab


From 9458b3e43acd6b3e68429a0f60206f499afaf999 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 02:10:59 -0500
Subject: [PATCH 133/752] Check how_user_voted before attempting to submit a
 vote.

---
 web/lib/forum.js | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 9cad6886b3..fc2e845340 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -506,14 +506,19 @@ function voteMessage(sub, number, up) {
         msgBase.close();
         return false;
     }
-    var vh = {
-        'from' : msgBase.cfg.settings&SUB_NAME ? user.name : user.alias,
-        'from_ext' : user.number,
-        'from_net_type' : NET_NONE,
-        'thread_back' : header.number,
-        'attr' : up ? MSG_UPVOTE : MSG_DOWNVOTE
-    };
-    var ret = msgBase.vote_msg(vh);
+    var uv = msgBase.how_user_voted(
+        header.number, msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
+    );
+    if (uv === 0) {
+        var vh = {
+            'from' : msgBase.cfg.settings&SUB_NAME ? user.name : user.alias,
+            'from_ext' : user.number,
+            'from_net_type' : NET_NONE,
+            'thread_back' : header.number,
+            'attr' : up ? MSG_UPVOTE : MSG_DOWNVOTE
+        };
+        var ret = msgBase.vote_msg(vh);
+    }
     msgBase.close();
     return ret;
 }
-- 
GitLab


From 3508a5b6175adc05d5cda457c753c54858564c55 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 11:42:06 -0500
Subject: [PATCH 134/752] submitPollAnswers - validate message number, verify
 that message is a poll and is open, that user hasn't voted, turn answers
 array into bitfield; It woiks.

---
 web/lib/forum.js | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index fc2e845340..21c1313b6f 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -527,22 +527,39 @@ function submitPollAnswers(sub, number, answers) {
     if (typeof msg_area.sub[sub] === 'undefined') return false;
     if (msg_area.sub[sub].settings&SUB_NOVOTING) return false;
     if (user.security.restrictions&UFLAG_V) return false;
+    number = parseInt(number);
+    if (isNaN(number)) return false;
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return false;
     var ret = false;
     var header = msgBase.get_msg_header(number);
-    if (header !== null && header.attr&MSG_POLL && !(header.auxattr&POLL_CLOSED)) {
+    if (header !== null &&
+        header.attr&MSG_POLL &&
+        !(header.auxattr&POLL_CLOSED) &&
+        answers.length > 0 &&
+        (   answers.length <= header.votes ||
+            (answers.length == 1 && header.votes == 0)
+        )
+    ) {
         var uv = msgBase.how_user_voted(
             number, msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
         );
         if (uv === 0) {
+            var a = 0;
+            answers.forEach(
+                function (e) {
+                    e = parseInt(e);
+                    if (isNaN(e) || e < 0 || e > 15) return;
+                    a|=(1<<e);
+                }
+            );
             ret = msgBase.vote_msg(
                 {   'from' : msgBase.cfg.settings&SUB_NAME ? user.name : user.alias,
                     'from_ext' : user.number,
                     'from_net_type' : NET_NONE,
                     'thread_back' : number,
                     'attr' : MSG_VOTE,
-                    'votes' : answers
+                    'votes' : a
                 }
             );
         }
-- 
GitLab


From dcbf60c134eb75563ee62645d9cce618384a1bfe Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 11:44:25 -0500
Subject: [PATCH 135/752] submitPollAnswers - was missing 'sub' argument; does
 what it says on the tin; handle response from server, disable & style inputs.

---
 web/root/js/forum.js | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index d85d751260..44332073af 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -381,13 +381,27 @@ function getVotesInThreads(sub) {
 	);
 }
 
-function submitPollAnswers(id) {
+function submitPollAnswers(sub, id) {
+	if ($('input[name="poll-' + id + '"]:checked').length < 1) return;
+	var answers = [];
 	$('input[name="poll-' + id + '"]:checked').each(
-		function () {
-			alert($(this).val());
+		function () { answers.push($(this).val()); }
+	);
+	answers = answers.join('&answer=');
+	$.getJSON(
+		'./api/forum.ssjs?call=submit-poll-answers&sub=' + sub + '&id=' + id + '&answer=' + answers,
+		function (data) {
+			$('input[name="poll-' + id + '"]').each(
+				function () {
+					$(this).attr('disabled', true);
+					if ($(this).prop('checked')) {
+						$(this).parent().parent().addClass('upvote-bg');
+					}
+				}
+			);
+			$('submit-poll-' + id).attr('disabled', true);
 		}
 	);
-	// async ./api/forum.ssjs?call=submit-poll-answers&sub=x&id=x&answer=x&answer=x...
 }
 
 function pollControl(id, count) {
-- 
GitLab


From 27e67a5dc3cfc362625230095c04863e744cd8bc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 11:45:19 -0500
Subject: [PATCH 136/752] Add POLL indicator badge to threads in thread list if
 OP is a poll; various other poll related shits.

---
 web/root/pages/001-forum.ssjs | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 8385d2ee5e..c366a41ded 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -55,15 +55,16 @@ var strings = {
 					up : '<span id="uv-%s" title="Upvotes - Parent / Thread Total" class="badge upvote-bg"><span class="glyphicon glyphicon-arrow-up"></span><span id="uv-count-%s">%s / %s</span></span>&nbsp;',
 					down : '<span id="dv-%s" title="Downvotes - Parent / Thread Total" class="badge downvote-bg"><span class="glyphicon glyphicon-arrow-down"></span><span id="dv-count-%s">%s / %s</span></span>',
 				},
+				poll : '<span class="badge">POLL</span>',
 				close : '</div></div>'
 			},
 			close : '</div></a>'
 		},
 		controls : {
 			post : '<button class="btn btn-default icon" aria-label="Post a new message" title="Post a new message" onclick="addNew(\'%s\')"><span class="glyphicon glyphicon-pencil"></span></button>',
-			scan_new : '<button id="scan-cfg-new" class="btn %s icon" aria-label="Scan for new messages" title="Scan for new messages" onclick="setScanCfg(\'%s\', 1)"><span class="glyphicon glyphicon-ok-sign"></span></button>',
+			scan_new : '<div class="pull-right"><button id="scan-cfg-new" class="btn %s icon" aria-label="Scan for new messages" title="Scan for new messages" onclick="setScanCfg(\'%s\', 1)"><span class="glyphicon glyphicon-ok-sign"></span></button>',
 			scan_you : '<button id="scan-cfg-youonly" class="btn %s icon" aria-label="Scan for new messages to you only" title="Scan for new messages to you only" onclick="setScanCfg(\'%s\', 2)"><span class="glyphicon glyphicon-user"></span></button>',
-			scan_off : '<button id="scan-cfg-off" class="btn %s icon" aria-label="Do not scan this sub" title="Do not scan this sub" onclick="setScanCfg(\'%s\', 0)"><span class="glyphicon glyphicon-ban-circle"></span></button>'
+			scan_off : '<button id="scan-cfg-off" class="btn %s icon" aria-label="Do not scan this sub" title="Do not scan this sub" onclick="setScanCfg(\'%s\', 0)"><span class="glyphicon glyphicon-ban-circle"></span></button></div>'
 		}
 	},
 	thread_view : {
@@ -108,9 +109,9 @@ var strings = {
 						close : '</ul>'
 					},
 					open : '<li class="%s"><label><input type="%s" name="poll-%s" value="%s">%s</label> %s</li>',
-					closed : '<li class="checkbox%s">%s %s</li>'
+					closed : '<li class="checkbox%s">%02d. %s %s</li>'
 				},
-				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(%s)">Vote</button>',
+				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(\'%s\', %s)">Vote</button>',
 				closed : 'This poll has been closed.',
 				disallowed : 'You cannot vote on this poll.',
 				voted : 'You already voted on this poll.'
@@ -230,7 +231,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 							format(
 								strings.message.body.poll.answer.closed,
 								pollData.answers&(1<<i) ? ' upvote-bg' : '',
-								e.data,
+								i + 1, e.data,
 								pollData.show_results ? header.tally[i] || 0 : ''
 							)
 						);
@@ -261,7 +262,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			} else if (user.alias == settings.guest || user.security.restrictions&UFLAG_V || msgBase.cfg.settings&SUB_NOVOTING) {
 				writeln(strings.message.body.poll.disallowed);
 			} else {
-				writeln(format(strings.message.body.poll.button, header.number, header.number));
+				writeln(format(strings.message.body.poll.button, header.number, http_request.query.sub[0], header.number));
 			}
 
 			if (header.votes > 1) {
@@ -434,6 +435,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				(unread == 0 ? '' : unread)
 			)
 		);
+		if (thread.messages[first].attr&MSG_POLL) {
+			writeln(strings.thread_list.item.badges.poll);
+		}
 		writeln(strings.thread_list.item.badges.close);
 
 		writeln(strings.thread_list.item.close);
-- 
GitLab


From b82ddf79e72f5c4fc268667daa5cc6716e54007b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 12:52:43 -0500
Subject: [PATCH 137/752] Better styling of poll list-groups and vote counts.

---
 web/root/pages/001-forum.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index c366a41ded..9396c39191 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -108,8 +108,8 @@ var strings = {
 						open : '<ul class="list-group">',
 						close : '</ul>'
 					},
-					open : '<li class="%s"><label><input type="%s" name="poll-%s" value="%s">%s</label> %s</li>',
-					closed : '<li class="checkbox%s">%02d. %s %s</li>'
+					open : '<li class="list-group-item %s"><label><input type="%s" name="poll-%s" value="%s">%s</label> <div class="pull-right">%s</div></li>',
+					closed : '<li class="list-group-item checkbox%s">%s) %s <div class="pull-right">%s</div></li>'
 				},
 				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(\'%s\', %s)">Vote</button>',
 				closed : 'This poll has been closed.',
-- 
GitLab


From 78ba5e5443067ddaf725df4cabccc50df1500762 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 15:01:12 -0500
Subject: [PATCH 138/752] Improved formatting of polls; refresh poll stats
 periodically, disable inputs if user has voted elsewhere (in another
 tab/window/browser or on the BBS); vote-count refresh doesn't work - header
 tally[] property is absent when using MsgBase.get_msg_header.

---
 web/lib/forum.js              | 15 +++++++---
 web/root/api/forum.ssjs       | 10 +++++++
 web/root/js/forum.js          | 20 +++++++++++++
 web/root/pages/001-forum.ssjs | 56 +++++++++++++++++++++++++----------
 4 files changed, 82 insertions(+), 19 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 21c1313b6f..5864c5bdcc 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -175,8 +175,10 @@ function getVotesInThreads(sub) {
 }
 
 function getUserPollData(sub, id) {
-    var ret = { answers : 0, show_results : false };
+    var ret = { answers : 0, tally : [], show_results : false };
     if (typeof msg_area.sub[sub] === 'undefined') return ret;
+    id = parseInt(id);
+    if (isNaN(id)) return ret;
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return ret;
     var header = msgBase.get_msg_header(id);
@@ -184,6 +186,7 @@ function getUserPollData(sub, id) {
         msgBase.close();
         return ret;
     }
+    if (header.tally && Array.isArray(header.tally)) ret.tally = header.tally;
     ret.answers = msgBase.how_user_voted(
         header.number,
         msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
@@ -493,7 +496,9 @@ function voteMessage(sub, number, up) {
     if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail') {
         return false;
     }
-    if (user.security.restrictions&UFLAG_V) return false;
+    if (user.alias == settings.guest || user.security.restrictions&UFLAG_V) {
+        return false;
+    }
     if (msg_area.sub[sub].settings&SUB_NOVOTING) return false;
     number = parseInt(number);
     if (isNaN(number)) return false;
@@ -502,7 +507,7 @@ function voteMessage(sub, number, up) {
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return false;
     var header = msgBase.get_msg_header(number);
-    if (header === null) {
+    if (header === null || header.attr&MSG_POLL) {
         msgBase.close();
         return false;
     }
@@ -526,7 +531,9 @@ function voteMessage(sub, number, up) {
 function submitPollAnswers(sub, number, answers) {
     if (typeof msg_area.sub[sub] === 'undefined') return false;
     if (msg_area.sub[sub].settings&SUB_NOVOTING) return false;
-    if (user.security.restrictions&UFLAG_V) return false;
+    if (user.alias == settings.guest || user.security.restrictions&UFLAG_V) {
+        return false;
+    }
     number = parseInt(number);
     if (isNaN(number)) return false;
     var msgBase = new MsgBase(sub);
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index c203ae023a..37d10b7e23 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -189,6 +189,16 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     reply = getVotesInThreads(http_request.query.sub[0]);
                 }
                 break;
+            case 'get-poll-results':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined'
+                ) {
+                    reply = getUserPollData(
+                        http_request.query.sub[0],
+                        http_request.query.id[0]
+                    );
+                }
+                break;
             default:
                 break;
         }
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 44332073af..ac06fa4126 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -422,4 +422,24 @@ function pollControl(id, count) {
 			);
 		}
 	);
+}
+
+function getPollData(sub, id) {
+	$.getJSON(
+		'./api/forum.ssjs?call=get-poll-results&sub=' + sub + '&id=' + id,
+		function (data) {
+			console.log(JSON.stringify(data, null, 2));
+			data.tally.forEach(
+				function (e, i) {
+					if (e > 0) $('#poll-count-' + id + '-' + i).text(e);
+				}
+			);
+			if (data.answers > 0) {
+				$('input[name="poll-' + id + '"]').each(
+					function () { $(this).attr('disabled', true); }
+				);
+				$('#submit-poll-' + id).attr('disabled', true);
+			}
+		}
+	);
 }
\ No newline at end of file
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 9396c39191..2c98524dc8 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -16,6 +16,7 @@ var strings = {
 		get_group_unread : 'getGroupUnreadCount("%s")',
 		get_sub_unread : 'getSubUnreadCount("%s")',
 		poll_control : 'pollControl("%s", %s)',
+		get_poll_data : 'getPollData("%s", %s)',
 		close : '</script>'
 	},
 	notice_box : '<div id="noticebox" class="alert alert-warning">%s<script type="text/javascript">$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});</script>',
@@ -102,15 +103,16 @@ var strings = {
 		body : {
 			open : '<div class="message" id="message-%s">',
 			poll : {
-				comment : '<strong>%s</strong>',
+				container : {
+					open : '<ul class="list-group">',
+					close : '</ul>'
+				},
+				comment : '<li class="list-group-item"><strong>%s</strong></li>',
 				answer : {
-					container : {
-						open : '<ul class="list-group">',
-						close : '</ul>'
-					},
-					open : '<li class="list-group-item %s"><label><input type="%s" name="poll-%s" value="%s">%s</label> <div class="pull-right">%s</div></li>',
-					closed : '<li class="list-group-item checkbox%s">%s) %s <div class="pull-right">%s</div></li>'
+					open : '<li class="list-group-item %s"><label><input type="%s" name="poll-%s" value="%s">%s</label> <div id="poll-count-%s-%s" class="pull-right">%s</div></li>',
+					closed : '<li class="list-group-item checkbox%s">%s) %s <div id="poll-count-%s-%s" class="pull-right">%s</div></li>'
 				},
+				last : '<li class="list-group-item">&nbsp;<div class="pull-right">%s</div></li>',
 				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(\'%s\', %s)">Vote</button>',
 				closed : 'This poll has been closed.',
 				disallowed : 'You cannot vote on this poll.',
@@ -216,13 +218,14 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 			var pollData = getUserPollData(http_request.query.sub[0], header.number);
 
+			writeln(strings.message.body.poll.container.open);
+
 			header.poll_comments.forEach(
 				function (e) {
 					writeln(format(strings.message.body.poll.comment, e.data));
 				}
 			);
 
-			writeln(strings.message.body.poll.answer.container.open);
 			// Poll is closed or user has voted
 			if (header.auxattr&POLL_CLOSED || pollData.answers > 0) {
 				header.poll_answers.forEach(
@@ -231,7 +234,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 							format(
 								strings.message.body.poll.answer.closed,
 								pollData.answers&(1<<i) ? ' upvote-bg' : '',
-								i + 1, e.data,
+								i + 1, e.data, header.number, i,
 								pollData.show_results ? header.tally[i] || 0 : ''
 							)
 						);
@@ -246,31 +249,54 @@ if (typeof http_request.query.sub !== 'undefined' &&
 								strings.message.body.poll.answer.open,
 								header.votes < 2 ? 'radio' : 'checkbox',
 								header.votes < 2 ? 'radio' : 'checkbox',
-								header.number, i, e.data,
+								header.number, i, e.data, header.number, i,
 								pollData.show_results ? header.tally[i] || 0 : ''
 							)
 						);
 					}
 				);
 			}
-			writeln(strings.message.body.poll.answer.container.close);
 
 			if (header.auxattr&POLL_CLOSED) {
-				writeln(strings.message.body.poll.closed);
+				writeln(format(strings.message.body.poll.last, strings.message.body.poll.closed));
 			} else if (pollData.answers > 0) {
-				writeln(strings.message.body.poll.voted);
+				writeln(format(strings.message.body.poll.last, strings.message.body.poll.voted));
 			} else if (user.alias == settings.guest || user.security.restrictions&UFLAG_V || msgBase.cfg.settings&SUB_NOVOTING) {
-				writeln(strings.message.body.poll.disallowed);
+				writeln(format(strings.message.body.poll.last, strings.message.body.poll.disallowed));
 			} else {
-				writeln(format(strings.message.body.poll.button, header.number, http_request.query.sub[0], header.number));
+				writeln(
+					format (
+						strings.message.body.poll.last,
+						format(
+							strings.message.body.poll.button,
+							header.number, http_request.query.sub[0], header.number
+						)
+					)
+				);
 			}
 
+			writeln(strings.message.body.poll.container.close);
+
 			if (header.votes > 1) {
 				writeln(strings.script.open);
 				writeln(format(strings.script.poll_control, header.number, header.votes));
 				writeln(strings.script.close);
 			}
 
+			// Refresh poll results every so often
+			writeln(strings.script.open);
+			writeln(
+				format(
+					strings.script.interval,
+					format(
+						strings.script.get_poll_data,
+						http_request.query.sub[0], header.number
+					),
+					settings.refresh_interval || 60000
+				)
+			);
+			writeln(strings.script.close);
+
 		// This is a normal message
 		} else {
 			writeln(formatMessage(body));
-- 
GitLab


From 9462b906d90bb395cb1c68086bc4ac8c5b16aa85 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 15:15:39 -0500
Subject: [PATCH 139/752] Add poll button & placeholder function.

---
 web/root/js/forum.js          |  4 ++++
 web/root/pages/001-forum.ssjs | 22 +++++++++++++++++-----
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index ac06fa4126..23223e6e4f 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -442,4 +442,8 @@ function getPollData(sub, id) {
 			}
 		}
 	);
+}
+
+function addPoll(sub) {
+	
 }
\ No newline at end of file
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 2c98524dc8..7a3b91d713 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -63,6 +63,7 @@ var strings = {
 		},
 		controls : {
 			post : '<button class="btn btn-default icon" aria-label="Post a new message" title="Post a new message" onclick="addNew(\'%s\')"><span class="glyphicon glyphicon-pencil"></span></button>',
+			post_poll : '<button class="btn btn-default icon" aria-label="Post a new poll" title="Post a new poll" onclick="addPoll(\'%s\')"><span class="glyphicon glyphicon-list-alt"></span></button>',
 			scan_new : '<div class="pull-right"><button id="scan-cfg-new" class="btn %s icon" aria-label="Scan for new messages" title="Scan for new messages" onclick="setScanCfg(\'%s\', 1)"><span class="glyphicon glyphicon-ok-sign"></span></button>',
 			scan_you : '<button id="scan-cfg-youonly" class="btn %s icon" aria-label="Scan for new messages to you only" title="Scan for new messages to you only" onclick="setScanCfg(\'%s\', 2)"><span class="glyphicon glyphicon-user"></span></button>',
 			scan_off : '<button id="scan-cfg-off" class="btn %s icon" aria-label="Do not scan this sub" title="Do not scan this sub" onclick="setScanCfg(\'%s\', 0)"><span class="glyphicon glyphicon-ban-circle"></span></button></div>'
@@ -185,7 +186,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		writeln(strings.message.header.details.close);
 
 		writeln(strings.message.header.voting.open);
-		if ((typeof settings.vote_buttons === 'undefined' || settings.vote_buttons) && !(header.attr&MSG_POLL)) {
+		if (!(msgBase.cfg.settings&SUB_NOVOTING) &&
+			(typeof settings.vote_buttons === 'undefined' || settings.vote_buttons) &&
+			!(header.attr&MSG_POLL)
+		) {
 			writeln(
 				format(
 					strings.message.header.voting.buttons.up,
@@ -204,7 +208,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 					header.downvotes
 				)
 			);
-		} else if (header.attr&MSG_POLL) {
+		} else if (!(msgBase.cfg.settings&SUB_NOVOTING) && header.attr&MSG_POLL) {
 			writeln(strings.message.header.voting.poll);
 		}
 		writeln(strings.message.header.voting.close);
@@ -214,7 +218,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		// Body
 		writeln(format(strings.message.body.open, header.number));
 		// If this is a poll message
-		if (header.attr&MSG_POLL) {
+		if (!(msgBase.cfg.settings&SUB_NOVOTING) && header.attr&MSG_POLL) {
 
 			var pollData = getUserPollData(http_request.query.sub[0], header.number);
 
@@ -433,7 +437,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		writeln(strings.thread_list.item.details.close);
 
 		writeln(strings.thread_list.item.badges.open);
-		if (settings.vote_buttons && (thread.votes.up > 0 || thread.votes.down > 0)) {
+		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
+			settings.vote_buttons &&
+			(thread.votes.up > 0 || thread.votes.down > 0)
+		) {
 			writeln(
 				format(
 					strings.thread_list.item.badges.votes.up,
@@ -461,7 +468,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				(unread == 0 ? '' : unread)
 			)
 		);
-		if (thread.messages[first].attr&MSG_POLL) {
+		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
+			thread.messages[first].attr&MSG_POLL
+		) {
 			writeln(strings.thread_list.item.badges.poll);
 		}
 		writeln(strings.thread_list.item.badges.close);
@@ -485,6 +494,9 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	if (user.alias !== settings.guest && msg_area.sub[http_request.query.sub[0]].can_post) {
 		writeln(format(strings.thread_list.controls.post, http_request.query.sub[0]));
+		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING)) {
+			writeln(format(strings.thread_list.controls.post_poll, http_request.query.sub[0]));
+		}
 	}
 
 	if (user.alias !== settings.guest) {
-- 
GitLab


From 9635ee511702f4fef427f2af95f24682e471c559 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 15:26:25 -0500
Subject: [PATCH 140/752] Add clearfix class to poll final list-item (vote
 button or status text); change vote button from btn-default to btn-primary
 class to make it more obvious.

---
 web/root/pages/001-forum.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 7a3b91d713..b6874ee7cd 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -113,8 +113,8 @@ var strings = {
 					open : '<li class="list-group-item %s"><label><input type="%s" name="poll-%s" value="%s">%s</label> <div id="poll-count-%s-%s" class="pull-right">%s</div></li>',
 					closed : '<li class="list-group-item checkbox%s">%s) %s <div id="poll-count-%s-%s" class="pull-right">%s</div></li>'
 				},
-				last : '<li class="list-group-item">&nbsp;<div class="pull-right">%s</div></li>',
-				button : '<button id="submit-poll-%s" class="btn btn-default" onclick="submitPollAnswers(\'%s\', %s)">Vote</button>',
+				last : '<li class="clearfix list-group-item">&nbsp;<div class="pull-right">%s</div></li>',
+				button : '<button id="submit-poll-%s" class="btn btn-primary" onclick="submitPollAnswers(\'%s\', %s)">Vote</button>',
 				closed : 'This poll has been closed.',
 				disallowed : 'You cannot vote on this poll.',
 				voted : 'You already voted on this poll.'
-- 
GitLab


From 5f89bb51ccecb333a9077a476c4290c850340ca3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 16:03:01 -0500
Subject: [PATCH 141/752] Formatting; use get_all_msg_headers for now to
 refresh poll stats

---
 web/lib/forum.js              | 14 ++++++++++++--
 web/root/js/forum.js          |  3 +--
 web/root/pages/001-forum.ssjs | 11 +++++++++--
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 5864c5bdcc..da2aeb7e77 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -181,7 +181,16 @@ function getUserPollData(sub, id) {
     if (isNaN(id)) return ret;
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return ret;
-    var header = msgBase.get_msg_header(id);
+    // var header = msgBase.get_msg_header(id);
+    // Temporary use of get_all_msg_headers() to get header.tally for polls
+    var headers = msgBase.get_all_msg_headers();
+    var header = null;
+    for (var h in headers) {
+        if (headers[h].number !== id) continue;
+        header = headers[h];
+        break;
+    }
+    // End of temporary shitfest
     if (header === null || !(header.attr&MSG_POLL)) {
         msgBase.close();
         return ret;
@@ -757,7 +766,7 @@ function setScanCfg(sub, cfg) {
 
 }
 
-var getMessageThreads = function(sub, max) {
+function getMessageThreads(sub, max) {
 
     var threads = { thread : {}, order : [] };
     var subjects = {};
@@ -805,6 +814,7 @@ var getMessageThreads = function(sub, max) {
             );
             threads.thread[thread_id].messages[header.number].votes = header.votes;
             threads.thread[thread_id].messages[header.number].tally = header.tally || [];
+            threads.thread[thread_id].messages[header.number].subject = header.subject;
         } else {
             threads.thread[thread_id].votes.up += (header.upvotes || 0);
             threads.thread[thread_id].votes.down += (header.downvotes || 0);
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 23223e6e4f..11ba915f11 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -428,7 +428,6 @@ function getPollData(sub, id) {
 	$.getJSON(
 		'./api/forum.ssjs?call=get-poll-results&sub=' + sub + '&id=' + id,
 		function (data) {
-			console.log(JSON.stringify(data, null, 2));
 			data.tally.forEach(
 				function (e, i) {
 					if (e > 0) $('#poll-count-' + id + '-' + i).text(e);
@@ -445,5 +444,5 @@ function getPollData(sub, id) {
 }
 
 function addPoll(sub) {
-	
+
 }
\ No newline at end of file
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index b6874ee7cd..add7e2b51d 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -108,7 +108,8 @@ var strings = {
 					open : '<ul class="list-group">',
 					close : '</ul>'
 				},
-				comment : '<li class="list-group-item"><strong>%s</strong></li>',
+				title : '<li class="list-group-item"><strong>%s</strong></li>',
+				comment : '<li class="list-group-item">%s</li>',
 				answer : {
 					open : '<li class="list-group-item %s"><label><input type="%s" name="poll-%s" value="%s">%s</label> <div id="poll-count-%s-%s" class="pull-right">%s</div></li>',
 					closed : '<li class="list-group-item checkbox%s">%s) %s <div id="poll-count-%s-%s" class="pull-right">%s</div></li>'
@@ -224,6 +225,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 			writeln(strings.message.body.poll.container.open);
 
+			writeln(format(strings.message.body.poll.title, header.subject));
+
 			header.poll_comments.forEach(
 				function (e) {
 					writeln(format(strings.message.body.poll.comment, e.data));
@@ -231,7 +234,11 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			);
 
 			// Poll is closed or user has voted
-			if (header.auxattr&POLL_CLOSED || pollData.answers > 0) {
+			if (header.auxattr&POLL_CLOSED ||
+				pollData.answers > 0 ||
+				user.alias == settings.guest ||
+				user.security.restrictions&UFLAG_V
+			) {
 				header.poll_answers.forEach(
 					function (e, i) {
 						writeln(
-- 
GitLab


From d272fe1e6cd6ac1439ac55f5bdde3e02fb12facf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 17:19:52 -0500
Subject: [PATCH 142/752] Moved scripts outside of the msg body div. :|

---
 web/root/pages/001-forum.ssjs | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index add7e2b51d..e3cb916d71 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -287,15 +287,13 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			}
 
 			writeln(strings.message.body.poll.container.close);
+			writeln(strings.message.body.close);
 
+			writeln(strings.script.open);
 			if (header.votes > 1) {
-				writeln(strings.script.open);
 				writeln(format(strings.script.poll_control, header.number, header.votes));
-				writeln(strings.script.close);
 			}
-
 			// Refresh poll results every so often
-			writeln(strings.script.open);
 			writeln(
 				format(
 					strings.script.interval,
@@ -311,8 +309,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		// This is a normal message
 		} else {
 			writeln(formatMessage(body));
+			writeln(strings.message.body.close);
 		}
-		writeln(strings.message.body.close);
 
 		var prev = index === 0 ? thread.messages[thread.__first].number : thread.messages[keys[index - 1]].number;
 		var next = key === thread.__last ? thread.messages[thread.__last].number : thread.messages[keys[index + 1]].number;
-- 
GitLab


From 1e383bc9e7bb12f035e4bf8ab022778a7b937f1b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 18:50:13 -0500
Subject: [PATCH 143/752] Check rest V before showing add-poll button.

---
 web/root/pages/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index e3cb916d71..326eb4c1d7 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -499,7 +499,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	if (user.alias !== settings.guest && msg_area.sub[http_request.query.sub[0]].can_post) {
 		writeln(format(strings.thread_list.controls.post, http_request.query.sub[0]));
-		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING)) {
+		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) && !(user.security.restrictions&UFLAG_V)) {
 			writeln(format(strings.thread_list.controls.post_poll, http_request.query.sub[0]));
 		}
 	}
-- 
GitLab


From 3aa991c01e81fe32626dd4958097e62be190bb58 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 22 Nov 2016 18:51:31 -0500
Subject: [PATCH 144/752] Incomplete add-poll stuff.

---
 web/root/api/forum.ssjs | 10 +++++
 web/root/js/forum.js    | 99 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 103 insertions(+), 6 deletions(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 37d10b7e23..aa3357b010 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -160,6 +160,16 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 }
                 break;
 
+            case 'submit-poll':
+                if (typeof http_request.query.subject !== 'undefined' &&
+                    typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.votes !== 'undefined' &&
+                    typeof http_request.query.answer !== 'undefined'
+                ) {
+                    reply = "barf";
+                }
+                break;
+
             default:
                 handled = false;
                 break;
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 11ba915f11..703b26b1d7 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -119,10 +119,101 @@ function addNew(sub) {
 	);
 	window.location.hash = '#newmessage';
 	$('#newmessage-body').keydown(
-		function (evt) {
-			evt.stopImmediatePropagation();
+		function (evt) { evt.stopImmediatePropagation(); }
+	);
+}
+
+function submitPoll(sub) {
+
+	if ($('input[name="newpoll-answers"]:checked').length !== 1) return;
+
+	var subject = $('#newpoll-subject').val();
+	if (subject.length < 1) return;
+
+	var answerCount = $('input[name="newpoll-answers"]:checked:first').val();
+	if (answerCount == 2) answerCount = $('input[name="newpoll-answer-count"]').val();
+	if (answerCount < 0 || answerCount > 15) return;
+
+	var answers = [];
+	for (var i = 0; i < 16; i++) {
+		if ($('#newpoll-answer-' + i).length < 1) continue;
+		var val = $('#newpoll-answer-' + i).val();
+		if (val.length < 1) continue;
+		answers.push(val);
+	}
+	if (answers.length < 1) return;
+
+	$.getJSON(
+		'./api/forum.ssjs?call=submit-poll&sub=' + sub + '&subject=' + subject + '&votes=' + answerCount + '&answer=' + answers.join('&answer='),
+		function (data) {
+			console.log(data);
+			$('#newpoll').remove();
 		}
 	);
+
+}
+
+function addQuestion(elem) {
+	var count = $('div[name="newpoll-answer"]').length;
+	if (count > 15) return;
+	var number = count + 1;
+	$(	'<div id="newpoll-answer-container-' + number + '" name="newpoll-answer" class="form-group">' +
+			'<label for="newpoll-answer-' + number + '" class="col-sm-2 control-label">Answer</label>' +
+			'<div class="col-sm-9">' +
+				'<input id="newpoll-answer-' + number + '" class="form-control" type="text"> ' +
+			'</div>' +
+			'<div class="col-sm-1">' +
+				'<button type="button" class="btn btn-danger" onclick="$(\'#newpoll-answer-container-' + number + '\').remove()">' +
+					'<span class="glyphicon glyphicon-remove"></span>' +
+				'</button> ' +
+			'</div>' +
+		'</div>'
+	).insertBefore(elem);
+	$('#newpoll-answer-' + number).keydown(
+		function (evt) { evt.stopImmediatePropagation(); }
+	);
+}
+
+function addPoll(sub) {
+	$('#forum-list-container').append(
+		'<li id="newpoll" class="list-group-item">' +
+			'<strong>Add a new poll</strong>' +
+			'<form id="newpoll-form" class="form-horizontal">' +
+				'<div class="form-group">' +
+					'<label for="newpoll-subject" class="col-sm-2 control-label">Subject</label>' +
+					'<div class="col-sm-10">' +
+						'<input id="newpoll-subject" class="form-control" type="text" placeholder="Subject">' +
+					'</div>' +
+				'</div>' +
+				'<div class="form-group">' +
+					'<label for="newpoll-answers" class="col-sm-2 control-label">Selection</label>' +
+					'<div class="col-sm-10">' +
+						'<label class="radio-inline">' +
+							'<input type="radio" name="newpoll-answers" value="1" checked> Single' +
+						'</label>' +
+						'<label class="radio-inline">' +
+							'<input type="radio" name="newpoll-answers" value="2"> Multiple ' +
+							'<input type="number" name="newpoll-answer-count" min="1" max="15" value="1">' +
+						'</label>' +
+					'</div>' +
+				'</div>' +
+				'<div id="newpoll-button" class="form-group">' +
+					'<div class="col-sm-offset-2 col-sm-10">' +
+						'<button id="newpoll-submit" type="button" class="btn btn-primary" onclick="submitPoll(\'' + sub + '\')">' +
+							'Submit' +
+						'</button>' +
+						'<div class="pull-right">' +
+							'<button type="button" class="btn btn-success" onclick="addQuestion(\'#newpoll-button\')">' +
+								'<span class="glypicon glyphicon-plus"></span>' +
+							'</button> ' +
+						'</div>' +
+				    '</div>' +
+				'</div>' +
+			'</form>' +
+		'</li>'
+	);
+	addQuestion('#newpoll-button');
+	window.location.hash = '#newpoll';
 }
 
 // Add a reply input form to the page for message with number 'id' in sub 'sub'
@@ -441,8 +532,4 @@ function getPollData(sub, id) {
 			}
 		}
 	);
-}
-
-function addPoll(sub) {
-
 }
\ No newline at end of file
-- 
GitLab


From 22f8b205ee4a7f5312effef28a201424988e6dfa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 01:06:31 -0500
Subject: [PATCH 145/752] Poll-posting things.

---
 web/lib/forum.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index da2aeb7e77..89d7e49510 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -475,6 +475,61 @@ function postReply(sub, body, pid) {
     return ret;
 }
 
+function postPoll(sub, subject, votes, results, answers, comments) {
+
+    if (user.alias == settings.guest || user.security.restrictions&UFLAG_V) {
+        return false;
+    }
+
+    if (typeof msg_area.sub[sub] === 'undefined' || !msg_area.sub[sub].can_post) {
+        return false;
+    }
+
+    if (typeof subject !== 'string' || subject.length < 1) return false;
+
+    if (!Array.isArray(answers) || answers.length < 2) return false;
+
+    votes = parseInt(votes);
+    if (isNaN(votes) || votes < 1 || votes > 15) return false;
+    if (votes > answers) votes = answers;
+
+    results = parseInt(results);
+    if (isNaN(results) || results < 0 || results > 3) {
+        return false;
+    }
+
+    var header = {
+        subject : subject,
+        from : msg_area.sub[sub].settings&SUB_NAME ? user.name : user.alias,
+        from_ext : user.number,
+        field_list : [],
+        auxattr : (results<<POLL_RESULTS_SHIFT),
+        votes : votes
+    };
+
+    if (Array.isArray(comments)) {
+        comments.forEach(
+            function (e) {
+                header.field_list.push({ type : SMB_COMMENT, data : e });
+            }
+        );
+    }
+
+    answers.forEach(
+        function (e) {
+            header.field_list.push({ type : SMB_POLL_ANSWER, data : e });
+        }
+    );
+
+    var msgBase = new MsgBase(sub);
+    if (!msgBase.open()) return false;
+    var ret = msgBase.add_poll(header);
+    msgBase.close();
+
+    return ret;
+
+}
+
 // Delete a message if
 // - This is the mail sub, and the message was sent by or to this user
 // - This is another sub on which the user is an operator
-- 
GitLab


From a2dfd978160b0257701bfafcc4e069e2a1f0282a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 01:07:00 -0500
Subject: [PATCH 146/752] Close the noticebox div; possibly some poll-related
 things.

---
 web/root/pages/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 326eb4c1d7..236f8b2043 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -19,7 +19,7 @@ var strings = {
 		get_poll_data : 'getPollData("%s", %s)',
 		close : '</script>'
 	},
-	notice_box : '<div id="noticebox" class="alert alert-warning">%s<script type="text/javascript">$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});</script>',
+	notice_box : '<div id="noticebox" class="alert alert-warning">%s<script type="text/javascript">$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});</script></div>',
 	group_list : {
 		breadcrumb : '<ol class="breadcrumb"><li><a href="./?page=%s">Forum</a></li></ol>',
 		container : {
-- 
GitLab


From 35fc1bedf0c2993688899af9f9cbf9da95cb7aa9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 01:07:16 -0500
Subject: [PATCH 147/752] Poll-posting things

---
 web/root/api/forum.ssjs | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index aa3357b010..063eca097f 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -164,9 +164,17 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 if (typeof http_request.query.subject !== 'undefined' &&
                     typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.votes !== 'undefined' &&
+                    typeof http_request.query.results !== 'undefined' &&
                     typeof http_request.query.answer !== 'undefined'
                 ) {
-                    reply = "barf";
+                    reply.success = postPoll(
+                        http_request.query.sub[0],
+                        http_request.query.subject[0],
+                        http_request.query.votes[0],
+                        http_request.query.results[0],
+                        http_request.query.answer,
+                        http_request.query.comment || []
+                    );
                 }
                 break;
 
-- 
GitLab


From f21f35fef8e151dcec1e0977f27a4eb209b57e7f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 01:08:18 -0500
Subject: [PATCH 148/752] A nice-looking poll-creation / posting thing which is
 pretty ugly underneath.

---
 web/root/js/forum.js | 107 +++++++++++++++++++++++++++++++++----------
 1 file changed, 83 insertions(+), 24 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 703b26b1d7..77678b36ce 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -125,6 +125,8 @@ function addNew(sub) {
 
 function submitPoll(sub) {
 
+	$('#newpoll-submit').attr('disabled', true);
+
 	if ($('input[name="newpoll-answers"]:checked').length !== 1) return;
 
 	var subject = $('#newpoll-subject').val();
@@ -134,57 +136,91 @@ function submitPoll(sub) {
 	if (answerCount == 2) answerCount = $('input[name="newpoll-answer-count"]').val();
 	if (answerCount < 0 || answerCount > 15) return;
 
+	var results = parseInt($('input[name="newpoll-results"]:checked').val());
+	if (results < 0 || results > 3) return;
+
 	var answers = [];
-	for (var i = 0; i < 16; i++) {
-		if ($('#newpoll-answer-' + i).length < 1) continue;
-		var val = $('#newpoll-answer-' + i).val();
-		if (val.length < 1) continue;
-		answers.push(val);
-	}
+	$('input[name="newpoll-answer-input"]').each(
+		function () {
+			var val = $(this).val();
+			if (val !== '') answers.push(val);
+		}
+	);
 	if (answers.length < 1) return;
 
+	var comments = [];
+	$('input[name="newpoll-comment-input"]').each(
+		function () {
+			var val = $(this).val();
+			if (val !== '') comments.push(val);
+		}
+	);
+
+	var url =
+		'./api/forum.ssjs?call=submit-poll' +
+		'&sub=' + sub +
+		'&subject=' + subject +
+		'&votes=' + answerCount +
+		'&results=' + results +
+		'&answer=' + answers.join('&answer=');
+
+	if (comments.length > 0) url += '&comment=' + comments.join('&comment=');
+
 	$.getJSON(
-		'./api/forum.ssjs?call=submit-poll&sub=' + sub + '&subject=' + subject + '&votes=' + answerCount + '&answer=' + answers.join('&answer='),
-		function (data) {
-			console.log(data);
-			$('#newpoll').remove();
+		url, function (data) {
+			$('#newpoll-submit').attr('disabled', false);
+			if (data.success) {
+				$('#newpoll').remove();
+				insertParam('notice', 'Your poll has been posted.');
+			}
 		}
 	);
 
 }
 
-function addQuestion(elem) {
-	var count = $('div[name="newpoll-answer"]').length;
-	if (count > 15) return;
+function addPollField(type, elem) {
+
+	var prefix = 'newpoll-' + type;
+
+	var count = $('div[name="' + prefix + '"]').length;
+	if (type === 'answer' && count > 15) return;
 	var number = count + 1;
-	$(	'<div id="newpoll-answer-container-' + number + '" name="newpoll-answer" class="form-group">' +
-			'<label for="newpoll-answer-' + number + '" class="col-sm-2 control-label">Answer</label>' +
+
+	$(elem).append(
+		'<div id="' + prefix + '-container-' + number + '" name="' + prefix + '" class="form-group">' +
+			'<label for="' + prefix + '-' + number + '" class="col-sm-2 control-label">' +
+				(type === 'answer' ? 'Answer' : 'Comment') +
+			'</label>' +
 			'<div class="col-sm-9">' +
-				'<input id="newpoll-answer-' + number + '" class="form-control" type="text"> ' +
+				'<input id="' + prefix + '-' + number + '" class="form-control" name="' + prefix + '-input" type="text"> ' +
 			'</div>' +
 			'<div class="col-sm-1">' +
-				'<button type="button" class="btn btn-danger" onclick="$(\'#newpoll-answer-container-' + number + '\').remove()">' +
+				'<button type="button" class="btn btn-danger" onclick="$(\'#' + prefix + '-container-' + number + '\').remove()">' +
 					'<span class="glyphicon glyphicon-remove"></span>' +
 				'</button> ' +
 			'</div>' +
 		'</div>'
-	).insertBefore(elem);
-	$('#newpoll-answer-' + number).keydown(
+	);
+
+	$('#' + prefix + '-' + number).keydown(
 		function (evt) { evt.stopImmediatePropagation(); }
 	);
+
 }
 
 function addPoll(sub) {
+	if ($('#newpoll').length > 0) return;
 	$('#forum-list-container').append(
 		'<li id="newpoll" class="list-group-item">' +
 			'<strong>Add a new poll</strong>' +
 			'<form id="newpoll-form" class="form-horizontal">' +
 				'<div class="form-group">' +
-					'<label for="newpoll-subject" class="col-sm-2 control-label">Subject</label>' +
+					'<label for="newpoll-subject" class="col-sm-2 control-label">Question</label>' +
 					'<div class="col-sm-10">' +
-						'<input id="newpoll-subject" class="form-control" type="text" placeholder="Subject">' +
+						'<input id="newpoll-subject" class="form-control" type="text" placeholder="Required">' +
 					'</div>' +
 				'</div>' +
+				'<div id="newpoll-comment-group"></div>' +
 				'<div class="form-group">' +
 					'<label for="newpoll-answers" class="col-sm-2 control-label">Selection</label>' +
 					'<div class="col-sm-10">' +
@@ -197,14 +233,35 @@ function addPoll(sub) {
 						'</label>' +
 					'</div>' +
 				'</div>' +
+				'<div class="form-group">' +
+					'<label for="newpoll-results" class="col-sm-2 control-label">Show results</label>' +
+					'<div class="col-sm-10">' +
+						'<label class="radio-inline">' +
+							'<input type="radio" name="newpoll-results" value="0" checked> Voters' +
+						'</label>' +
+						'<label class="radio-inline">' +
+							'<input type="radio" name="newpoll-results" value="1">  Everyone' +
+						'</label>' +
+						'<label class="radio-inline">' +
+							'<input type="radio" name="newpoll-results" value="2"> Me Only (Until closed) ' +
+						'</label>' +
+						'<label class="radio-inline">' +
+							'<input type="radio" name="newpoll-results" value="3"> Me Only ' +
+						'</label>' +
+					'</div>' +
+				'</div>' +
+				'<div id="newpoll-answer-group"></div>' +
 				'<div id="newpoll-button" class="form-group">' +
 					'<div class="col-sm-offset-2 col-sm-10">' +
 						'<button id="newpoll-submit" type="button" class="btn btn-primary" onclick="submitPoll(\'' + sub + '\')">' +
 							'Submit' +
 						'</button>' +
 						'<div class="pull-right">' +
-							'<button type="button" class="btn btn-success" onclick="addQuestion(\'#newpoll-button\')">' +
-								'<span class="glypicon glyphicon-plus"></span>' +
+							'<button type="button" title="Add another comment" class="btn btn-success" onclick="addPollField(\'comment\', \'#newpoll-comment-group\')">' +
+								'<span class="glyphicon glyphicon-pencil"></span>' +
+							'</button> ' +
+							'<button type="button" title="Add another answer" class="btn btn-success" onclick="addPollField(\'answer\', \'#newpoll-answer-group\')">' +
+								'<span class="glyphicon glyphicon-plus"></span>' +
 							'</button> ' +
 						'</div>' +
 				    '</div>' +
@@ -212,7 +269,9 @@ function addPoll(sub) {
 			'</form>' +
 		'</li>'
 	);
-	addQuestion('#newpoll-button');
+	addPollField('comment', '#newpoll-comment-group');
+	addPollField('answer', '#newpoll-answer-group');
+	addPollField('answer', '#newpoll-answer-group');
 	window.location.hash = '#newpoll';
 }
 
-- 
GitLab


From e104be61fc5cf403f99b318a532040f886edd522 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 09:07:27 -0500
Subject: [PATCH 149/752] Enforce a maximum length (LEN_TITLE) on poll subject,
 comments, and answers.

---
 web/lib/forum.js     | 10 +++++++---
 web/root/js/forum.js |  4 ++--
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 89d7e49510..0f096d3d47 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -499,7 +499,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
     }
 
     var header = {
-        subject : subject,
+        subject : subject.substr(0, LEN_TITLE),
         from : msg_area.sub[sub].settings&SUB_NAME ? user.name : user.alias,
         from_ext : user.number,
         field_list : [],
@@ -510,14 +510,18 @@ function postPoll(sub, subject, votes, results, answers, comments) {
     if (Array.isArray(comments)) {
         comments.forEach(
             function (e) {
-                header.field_list.push({ type : SMB_COMMENT, data : e });
+                header.field_list.push(
+                    { type : SMB_COMMENT, data : e.substr(0, LEN_TITLE) }
+                );
             }
         );
     }
 
     answers.forEach(
         function (e) {
-            header.field_list.push({ type : SMB_POLL_ANSWER, data : e });
+            header.field_list.push(
+                { type : SMB_POLL_ANSWER, data : e.substr(0, LEN_TITLE) }
+            );
         }
     );
 
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 77678b36ce..d32cd3e518 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -192,7 +192,7 @@ function addPollField(type, elem) {
 				(type === 'answer' ? 'Answer' : 'Comment') +
 			'</label>' +
 			'<div class="col-sm-9">' +
-				'<input id="' + prefix + '-' + number + '" class="form-control" name="' + prefix + '-input" type="text"> ' +
+				'<input id="' + prefix + '-' + number + '" class="form-control" name="' + prefix + '-input" type="text" maxlength="70"> ' +
 			'</div>' +
 			'<div class="col-sm-1">' +
 				'<button type="button" class="btn btn-danger" onclick="$(\'#' + prefix + '-container-' + number + '\').remove()">' +
@@ -217,7 +217,7 @@ function addPoll(sub) {
 				'<div class="form-group">' +
 					'<label for="newpoll-subject" class="col-sm-2 control-label">Question</label>' +
 					'<div class="col-sm-10">' +
-						'<input id="newpoll-subject" class="form-control" type="text" placeholder="Required">' +
+						'<input id="newpoll-subject" class="form-control" type="text" placeholder="Required" maxlength="70">' +
 					'</div>' +
 				'</div>' +
 				'<div id="newpoll-comment-group"></div>' +
-- 
GitLab


From a7250b1dd8f14b2e20705d6fe9504ec726507268 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 09:43:18 -0500
Subject: [PATCH 150/752] Renamed vote_buttons setting to vote_functions

---
 README.md                     | 2 +-
 web/root/pages/001-forum.ssjs | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index f027840f0d..500c9b8bbf 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ A web interface for Synchronet BBS
 	; Enable or disable keyboard navigation in message threads
 	keyboard_navigation = false
 	; Display upvote/downvote buttons in message threads (3.17)
-	vote_buttons = true
+	vote_functions = true
 	; Refresh nodelist, vote counts, etc. this often (in milliseconds)
 	refresh_interval = 60000
 ```
diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 236f8b2043..36a4299e6f 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -10,7 +10,7 @@ var strings = {
 		open : '<script type="text/javascript">',
 		thread_navigation : 'threadNav();',
 		interval : 'setInterval(function () { %s }, %s);',
-		vote_buttons : 'enableVoteButtonHandlers("%s");',
+		vote_functions : 'enableVoteButtonHandlers("%s");',
 		vote_refresh_thread : 'getVotesInThread("%s", %s)',
 		vote_refresh_threads : 'getVotesInThreads("%s")',
 		get_group_unread : 'getGroupUnreadCount("%s")',
@@ -188,7 +188,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		writeln(strings.message.header.voting.open);
 		if (!(msgBase.cfg.settings&SUB_NOVOTING) &&
-			(typeof settings.vote_buttons === 'undefined' || settings.vote_buttons) &&
+			(typeof settings.vote_functions === 'undefined' || settings.vote_functions) &&
 			!(header.attr&MSG_POLL)
 		) {
 			writeln(
@@ -382,7 +382,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	writeln(strings.script.open);
 	if (settings.keyboard_navigation) writeln(strings.script.thread_navigation);
 	if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
-		writeln(format(strings.script.vote_buttons, http_request.query.sub[0]));
+		writeln(format(strings.script.vote_functions, http_request.query.sub[0]));
 	}
 	writeln(
 		format(
@@ -443,7 +443,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		writeln(strings.thread_list.item.badges.open);
 		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
-			settings.vote_buttons &&
+			settings.vote_functions &&
 			(thread.votes.up > 0 || thread.votes.down > 0)
 		) {
 			writeln(
-- 
GitLab


From f2841bba7a3c5d2fa6fb3ef4d417e2145978aaeb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 10:09:50 -0500
Subject: [PATCH 151/752] A little more respeck for the vote_functions setting.

---
 web/root/pages/001-forum.ssjs | 88 ++++++++++++++++++++++-------------
 1 file changed, 55 insertions(+), 33 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 36a4299e6f..fb1a3d4734 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -155,6 +155,11 @@ if (typeof http_request.query.sub !== 'undefined' &&
 	function writeMessage(thread, keys, key, index) {
 
 		var header = thread.messages[key];
+		if (header.attr&MSG_POLL &&
+			(msgBase.cfg.settings&SUB_NOVOTING || !settings.vote_functions)
+		) {
+			return;
+		}
 
 		var body = msgBase.get_msg_body(header.number);
 		if (body === null) return;
@@ -187,8 +192,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		writeln(strings.message.header.details.close);
 
 		writeln(strings.message.header.voting.open);
-		if (!(msgBase.cfg.settings&SUB_NOVOTING) &&
-			(typeof settings.vote_functions === 'undefined' || settings.vote_functions) &&
+		if (settings.vote_functions &&
+			!(msgBase.cfg.settings&SUB_NOVOTING) &&
 			!(header.attr&MSG_POLL)
 		) {
 			writeln(
@@ -209,7 +214,11 @@ if (typeof http_request.query.sub !== 'undefined' &&
 					header.downvotes
 				)
 			);
-		} else if (!(msgBase.cfg.settings&SUB_NOVOTING) && header.attr&MSG_POLL) {
+		} else if (
+			settings.vote_functions &&
+			!(msgBase.cfg.settings&SUB_NOVOTING) &&
+			header.attr&MSG_POLL
+		) {
 			writeln(strings.message.header.voting.poll);
 		}
 		writeln(strings.message.header.voting.close);
@@ -219,7 +228,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		// Body
 		writeln(format(strings.message.body.open, header.number));
 		// If this is a poll message
-		if (!(msgBase.cfg.settings&SUB_NOVOTING) && header.attr&MSG_POLL) {
+		if (settings.vote_functions &&
+			!(msgBase.cfg.settings&SUB_NOVOTING) &&
+			header.attr&MSG_POLL
+		) {
 
 			var pollData = getUserPollData(http_request.query.sub[0], header.number);
 
@@ -381,25 +393,29 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	writeln(strings.script.open);
 	if (settings.keyboard_navigation) writeln(strings.script.thread_navigation);
-	if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
-		writeln(format(strings.script.vote_functions, http_request.query.sub[0]));
-	}
-	writeln(
-		format(
-			strings.script.vote_refresh_thread,
-			http_request.query.sub[0], thread.__first
-		)
-	);
-	writeln(
-		format(
-			strings.script.interval,
+	if (settings.vote_functions) {
+		if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
+			writeln(
+				format(strings.script.vote_functions, http_request.query.sub[0])
+			);
+		}
+		writeln(
 			format(
 				strings.script.vote_refresh_thread,
 				http_request.query.sub[0], thread.__first
-			),
-			settings.refresh_interval || 60000
-		)
-	);
+			)
+		);
+		writeln(
+			format(
+				strings.script.interval,
+				format(
+					strings.script.vote_refresh_thread,
+					http_request.query.sub[0], thread.__first
+				),
+				settings.refresh_interval || 60000
+			)
+		);
+	}
 	writeln(strings.script.close);
 
 } else if (
@@ -442,8 +458,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		writeln(strings.thread_list.item.details.close);
 
 		writeln(strings.thread_list.item.badges.open);
-		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
-			settings.vote_functions &&
+		if (settings.vote_functions &&
+			!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
 			(thread.votes.up > 0 || thread.votes.down > 0)
 		) {
 			writeln(
@@ -473,7 +489,8 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				(unread == 0 ? '' : unread)
 			)
 		);
-		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
+		if (settings.vote_functions &&
+			!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
 			thread.messages[first].attr&MSG_POLL
 		) {
 			writeln(strings.thread_list.item.badges.poll);
@@ -499,7 +516,10 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 	if (user.alias !== settings.guest && msg_area.sub[http_request.query.sub[0]].can_post) {
 		writeln(format(strings.thread_list.controls.post, http_request.query.sub[0]));
-		if (!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) && !(user.security.restrictions&UFLAG_V)) {
+		if (settings.vote_functions &&
+			!(msg_area.sub[http_request.query.sub[0]].settings&SUB_NOVOTING) &&
+			!(user.security.restrictions&UFLAG_V)
+		) {
 			writeln(format(strings.thread_list.controls.post_poll, http_request.query.sub[0]));
 		}
 	}
@@ -537,15 +557,17 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		log(LOG_WARNING, err);
 	}
 
-	writeln(strings.script.open);
-	writeln(
-		format(
-			strings.script.interval,
-			format(strings.script.vote_refresh_threads, http_request.query.sub[0]),
-			settings.refresh_interval || 60000
-		)
-	);
-	writeln(strings.script.close);
+	if (settings.vote_functions) {
+		writeln(strings.script.open);
+		writeln(
+			format(
+				strings.script.interval,
+				format(strings.script.vote_refresh_threads, http_request.query.sub[0]),
+				settings.refresh_interval || 60000
+			)
+		);
+		writeln(strings.script.close);
+	}
 
 } else if (
 	typeof http_request.query.group !== 'undefined' &&
-- 
GitLab


From 8d70cee1780d523f10c4b4b85345969f4e5d0134 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 10:13:30 -0500
Subject: [PATCH 152/752] Forum is no longer compatible with the 3.16 release.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 500c9b8bbf..e4b9c5b75e 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ A web interface for Synchronet BBS
 
 ###Requirements
 
-- This web interface has been tested with Synchronet BBS 3.16c and 3.17.  It will probably work with earlier and later versions.
+- This web interface has been tested with Synchronet BBS 3.17a.  It will probably work with later versions.
 
 - The *Files* page of this web interface relies on a script which was introduced *after* the release of Synchronet BBS 3.16c.  You can grab a copy of *filebase.js* [here](http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/filebase.js?revision=1.7), and you should place it in your *exec/load/* directory.
 
-- 
GitLab


From 4b29cb29d83fab787025653482a986813995571c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 11:55:17 -0500
Subject: [PATCH 153/752] Removed a 3.16c note, added a note about updating.

---
 README.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/README.md b/README.md
index e4b9c5b75e..55b7e6bc6c 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,7 @@ A web interface for Synchronet BBS
 ###Requirements
 
 - This web interface has been tested with Synchronet BBS 3.17a.  It will probably work with later versions.
-
-- The *Files* page of this web interface relies on a script which was introduced *after* the release of Synchronet BBS 3.16c.  You can grab a copy of *filebase.js* [here](http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/filebase.js?revision=1.7), and you should place it in your *exec/load/* directory.
+- In addition to updating your Synchronet build, be sure to update your the javascript modules in your *exec* and *exec/load* directories as well.  You can get them from [the Synchronet CVS repository](http://cvs.synchro.net/) or in the [sbbs_run.zip archive](http://vert.synchro.net/Synchronet/sbbs_run.zip).
 
 ###Quick start
 
-- 
GitLab


From 6d19594c972aa1a2cc996c35e7ceb09fddf7e584 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 13:07:25 -0500
Subject: [PATCH 154/752] Bugfix re: counting unread messages in a sub.

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 0f096d3d47..bdb1d5d39c 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -62,7 +62,7 @@ function getSubUnreadCount(sub) {
     try {
         var msgBase = new MsgBase(sub);
         msgBase.open();
-        for (var m = msg_area.sub[sub].scan_ptr + 1; m < msgBase.last_msg; m++) {
+        for (var m = msg_area.sub[sub].scan_ptr + 1; m <= msgBase.last_msg; m++) {
             var i = msgBase.get_msg_index(m);
             if (i === null || i.attr&MSG_DELETE || i.attr&MSG_NODISP) continue;
             if ((   msg_area.sub[sub].scan_cfg&SCAN_CFG_YONLY &&
-- 
GitLab


From 927b79d812351dd9b5b75d869d60303202994808 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 17:56:33 -0500
Subject: [PATCH 155/752] In thread view, just use 'All' if a message header's
 'to' property is empty/missing.

---
 web/root/pages/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index fb1a3d4734..a8b1018f26 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -185,7 +185,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				strings.message.header.details.info,
 				header.from,
 				typeof header.from_net_addr === 'undefined' ? '' : '@' + header.from_net_addr,
-				header.to,
+				header.to || 'All',
 				(new Date(header.when_written_time * 1000)).toLocaleString()
 			)
 		);
-- 
GitLab


From ab3d54fc735c2b935ec03fc9c7e8ad0b185edc4e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 18:55:54 -0500
Subject: [PATCH 156/752] Set the header 'to' property to 'All' when posting a
 poll.

---
 web/lib/forum.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index bdb1d5d39c..e85dbfe1f8 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -502,6 +502,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
         subject : subject.substr(0, LEN_TITLE),
         from : msg_area.sub[sub].settings&SUB_NAME ? user.name : user.alias,
         from_ext : user.number,
+        to : 'All',
         field_list : [],
         auxattr : (results<<POLL_RESULTS_SHIFT),
         votes : votes
-- 
GitLab


From 427406b2090485bda1eedb7f28e3b59419f21d76 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 23 Nov 2016 19:22:09 -0500
Subject: [PATCH 157/752] Don't try to store upvotes/downvotes for poll
 messages when sorting threads.

---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index e85dbfe1f8..0341efddf1 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -845,8 +845,8 @@ function getMessageThreads(sub, max) {
             from_net_addr : header.from_net_addr,
             to : header.to,
             when_written_time : header.when_written_time,
-            upvotes : header.upvotes || 0,
-            downvotes : header.downvotes || 0
+            upvotes : (header.attr&MSG_POLL ? 0 : (header.upvotes || 0)),
+            downvotes : (header.attr&MSG_POLL ? 0 : (header.downvotes || 0))
         };
         if (header.attr&MSG_POLL) {
             header.field_list.sort(
-- 
GitLab


From 38365fd5269b64bbdab29c9f44c1248005c8bae8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 7 Dec 2016 17:34:17 -0500
Subject: [PATCH 158/752] Fixed busted file transfer crap.

---
 web/root/api/files.ssjs | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 22d8eac5c4..09d7dea588 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -4,10 +4,8 @@ load(settings.web_lib + 'files.js');
 load('filebase.js');
 
 var reply = {};
-
 if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-	typeof http_request.query.call !== 'undefined' &&
-	user.number > 0	&& user.alias !== settings.guest
+	typeof http_request.query.call !== 'undefined' && user.number > 0
 ) {
 
 	switch (http_request.query.call[0].toLowerCase()) {
@@ -30,16 +28,15 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 					}
 				);
 				if (file === null) break;
-				client.socket.send('HTTP/1.0 Status: 200 OK\r\n');
-				client.socket.send('Content-Type: application/octet-stream\r\n');
-				client.socket.send('Content-Disposition: attachment; filename="' + file.base + '.' + file.ext + '";\r\n');
-				client.socket.send('Content-Transfer-Encoding: binary\r\n');
-				client.socket.send('Content-Length: ' + file_size(file.path) + '\r\n');
-				client.socket.send('\r\n');
+				http_reply.header['Content-Type'] = 'application/octet-stream';
+				http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
+				http_reply.header['Content-Encoding'] = 'binary';
+				http_reply.header['Content-Length'] = file_size(file.path);
 				var f = new File(file.path);
-				f.open('r+b');
-				while (!f.eof) {
-					client.socket.sendBin(f.readBin(1), 1);
+				f.open('rb');
+				for (var n = 0; n < file_size(file.path); n++) {
+					var r = f.length - f.position;
+					write(f.read(r > 512 ? 512 : r));
 				}
 				f.close();
 				reply = false;
-- 
GitLab


From cbe7aea4d94b6b57f4948343fa43f0ca8ab93f80 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 7 Dec 2016 17:39:34 -0500
Subject: [PATCH 159/752] Moved stuff around to log a warning instead of an
 error if client browses a non-existent thread.

---
 web/root/pages/001-forum.ssjs | 49 ++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 24 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index a8b1018f26..c1d4427a46 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -387,36 +387,37 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			msg_area.sub[http_request.query.sub[0]].scan_ptr =
 			thread.messages[thread.__last].number;
 		}
-	} catch (err) {
-		log(LOG_WARNING, err);
-	}
 
-	writeln(strings.script.open);
-	if (settings.keyboard_navigation) writeln(strings.script.thread_navigation);
-	if (settings.vote_functions) {
-		if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
+		writeln(strings.script.open);
+		if (settings.keyboard_navigation) writeln(strings.script.thread_navigation);
+		if (settings.vote_functions) {
+			if (user.alias != settings.guest || user.security.restrictions&UFLAG_V) {
+				writeln(
+					format(strings.script.vote_functions, http_request.query.sub[0])
+				);
+			}
 			writeln(
-				format(strings.script.vote_functions, http_request.query.sub[0])
-			);
-		}
-		writeln(
-			format(
-				strings.script.vote_refresh_thread,
-				http_request.query.sub[0], thread.__first
-			)
-		);
-		writeln(
-			format(
-				strings.script.interval,
 				format(
 					strings.script.vote_refresh_thread,
 					http_request.query.sub[0], thread.__first
-				),
-				settings.refresh_interval || 60000
-			)
-		);
+				)
+			);
+			writeln(
+				format(
+					strings.script.interval,
+					format(
+						strings.script.vote_refresh_thread,
+						http_request.query.sub[0], thread.__first
+					),
+					settings.refresh_interval || 60000
+				)
+			);
+		}
+		writeln(strings.script.close);
+
+	} catch (err) {
+		log(LOG_WARNING, err);
 	}
-	writeln(strings.script.close);
 
 } else if (
 	typeof http_request.query.sub !== 'undefined' &&
-- 
GitLab


From 03bb6dffa68893a9a577ec9e865f5ca4ecff9f3f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 7 Dec 2016 17:45:05 -0500
Subject: [PATCH 160/752] Proper comparison of POLL_RESULTS stuff in
 header.auxattr

---
 web/lib/forum.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 0341efddf1..e9e16158a0 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -201,13 +201,14 @@ function getUserPollData(sub, id) {
         msgBase.cfg.settings&SUB_NAME ? user.name : user.alias
     );
     msgBase.close();
+    var pollAttr = header.auxattr&POLL_RESULTS_MASK;
     if (header.from === user.alias || header.from === user.name) {
         ret.show_results = true;
-    } else if (header.auxattr&POLL_RESULTS_CLOSED && header.auxattr&POLL_CLOSED) {
+    } else if (pollAttr === POLL_RESULTS_CLOSED && header.auxattr&POLL_CLOSED) {
         ret.show_results = true;
-    } else if (header.auxattr&POLL_RESULTS_OPEN) {
+    } else if (pollAttr === POLL_RESULTS_OPEN) {
         ret.show_results = true;
-    } else if (header.auxattr&POLL_RESULTS_VOTERS && ret.answers > 0) {
+    } else if (pollAttr === POLL_RESULTS_VOTERS && ret.answers > 0) {
         ret.show_results = true;
     }
     return ret;
-- 
GitLab


From b95fd7a36bcbd2ce4957a02054f4507221a6ec72 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 00:23:55 -0500
Subject: [PATCH 161/752] Don't melt nobody's compooters mkay?

---
 web/root/api/files.ssjs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 09d7dea588..1fcdd98cc6 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -3,6 +3,8 @@ load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'files.js');
 load('filebase.js');
 
+var CHUNK_SIZE = 1024;
+
 var reply = {};
 if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	typeof http_request.query.call !== 'undefined' && user.number > 0
@@ -34,9 +36,10 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				http_reply.header['Content-Length'] = file_size(file.path);
 				var f = new File(file.path);
 				f.open('rb');
-				for (var n = 0; n < file_size(file.path); n++) {
+				for (var n = 0; n < f.length; n += CHUNK_SIZE) {
 					var r = f.length - f.position;
-					write(f.read(r > 512 ? 512 : r));
+					write(f.read(r > CHUNK_SIZE ? CHUNK_SIZE : r));
+					yield(false);
 				}
 				f.close();
 				reply = false;
-- 
GitLab


From 4c340bbfdf13b12383466997efb09a69bec86b74 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 15:31:33 -0500
Subject: [PATCH 162/752] Added pre.ansi for extra file-listing cheesiness.

---
 web/root/css/style.css | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 5cc44f601a..eb7efa1709 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -64,6 +64,11 @@ input.dropdown {
 	margin: 0 0 1em 0;
 }
 
+pre.ansi {
+	background-color : black;
+	font-family : Courier New, monospace;
+}
+
 .icon {
 	margin: 0 1em 1em 0;
 }
-- 
GitLab


From d3ac31c20d24dfc7b0e8f278bd960dc3b78565a5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 15:34:10 -0500
Subject: [PATCH 163/752] If extended file description contains extended ascii
 or an escape sequence, run it through html_encode and put it in a pre block
 of the 'ansi' class, otherwise just put it in a plain pre (if it exists);
 people should update their exec/load/filebase.js to fix an
 extended-description loading bug.

---
 web/root/pages/002-files.ssjs | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index 299d05fc12..5d29f5411d 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -28,6 +28,13 @@ if (typeof http_request.query.dir !== 'undefined' &&
 	);
 
 	function writeFileDetails(file) {
+		if (typeof file.extdesc === 'undefined') {
+			file.extdesc = '';
+		} else if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) {
+			file.extdesc = '<pre class="ansi">' + html_encode(file.extdesc, true, false, true, true) + '</pre>';
+		} else {
+			file.extdesc = '<pre>' + file.extdesc + '</pre>';
+		}
 		writeln(
 			format(
 				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
@@ -36,13 +43,8 @@ if (typeof http_request.query.dir !== 'undefined' &&
 				'<p>%s</p>' +
 				'%s' +
 				'</a>',
-				http_request.query.dir[0],
-				file.name,
-				file.name,
-				file.size,
-				system.timestr(file.uldate),
-				file.desc,
-				typeof file.extdesc === 'undefined' ? "" : ("<p>" + file.extdesc + "</p>")
+				http_request.query.dir[0], file.name, file.name, file.size,
+				system.timestr(file.uldate), file.desc, file.extdesc
 			)
 		);
 	}
-- 
GitLab


From c697354aed916b7f1aa8af202996198840c2d16d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 15:44:48 -0500
Subject: [PATCH 164/752] Reduce line-height of pre.ansi for more gooder
 cheesiness

---
 web/root/css/style.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index eb7efa1709..a35d26777c 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -67,6 +67,7 @@ input.dropdown {
 pre.ansi {
 	background-color : black;
 	font-family : Courier New, monospace;
+	line-height : 1;
 }
 
 .icon {
-- 
GitLab


From ee0c7ec7314a59d80bfc16b4b59debe6418672dc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 16:21:05 -0500
Subject: [PATCH 165/752] Added pre.list class, inherits background colour from
 parent element; non-ANSI file extdescs are put in pre.list blocks.

---
 web/root/css/style.css        | 5 +++++
 web/root/pages/002-files.ssjs | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index a35d26777c..cff02b46a8 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -70,6 +70,11 @@ pre.ansi {
 	line-height : 1;
 }
 
+pre.list {
+	background-color : inherit;
+	border : none;
+}
+
 .icon {
 	margin: 0 1em 1em 0;
 }
diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index 5d29f5411d..d4f84d1f8e 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -33,7 +33,7 @@ if (typeof http_request.query.dir !== 'undefined' &&
 		} else if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) {
 			file.extdesc = '<pre class="ansi">' + html_encode(file.extdesc, true, false, true, true) + '</pre>';
 		} else {
-			file.extdesc = '<pre>' + file.extdesc + '</pre>';
+			file.extdesc = '<pre class="list">' + file.extdesc + '</pre>';
 		}
 		writeln(
 			format(
-- 
GitLab


From f3ff0dc37c17e20d8e778fbddcc23fcd5c971563 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 16:27:45 -0500
Subject: [PATCH 166/752] Also inherit foreground colour in pre.list

---
 web/root/css/style.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index cff02b46a8..6b8f41f52d 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -71,6 +71,7 @@ pre.ansi {
 }
 
 pre.list {
+	color : inherit;
 	background-color : inherit;
 	border : none;
 }
-- 
GitLab


From 6e6ae3889c9c02ffc672bb9ec8766727bd08047f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 16:42:58 -0500
Subject: [PATCH 167/752] Skip the short description if there's an extended one
 available

---
 web/root/pages/002-files.ssjs | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index d4f84d1f8e..11a4e143fd 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -28,23 +28,23 @@ if (typeof http_request.query.dir !== 'undefined' &&
 	);
 
 	function writeFileDetails(file) {
-		if (typeof file.extdesc === 'undefined') {
-			file.extdesc = '';
-		} else if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) {
-			file.extdesc = '<pre class="ansi">' + html_encode(file.extdesc, true, false, true, true) + '</pre>';
-		} else {
-			file.extdesc = '<pre class="list">' + file.extdesc + '</pre>';
+		var description = '<p>' + file.desc + '</p>';
+		if (typeof file.extdesc !== 'undefined') {
+			if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) {
+				description = '<pre class="ansi">' + html_encode(file.extdesc, true, false, true, true) + '</pre>';
+			} else {
+				description = '<pre class="list">' + file.extdesc + '</pre>';
+			}
 		}
 		writeln(
 			format(
 				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
 				'<strong>%s</strong> (%s)' +
 				'<p><em>Uploaded %s</em></p>' +
-				'<p>%s</p>' +
 				'%s' +
 				'</a>',
 				http_request.query.dir[0], file.name, file.name, file.size,
-				system.timestr(file.uldate), file.desc, file.extdesc
+				system.timestr(file.uldate), description
 			)
 		);
 	}
-- 
GitLab


From 0585363224f44a39404c9cf6c0b2bb7cecd343f2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 8 Dec 2016 20:08:04 -0500
Subject: [PATCH 168/752] Strip some crap out of plaintext extended file
 descriptions.

---
 web/root/pages/002-files.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
index 11a4e143fd..ab6069b681 100644
--- a/web/root/pages/002-files.ssjs
+++ b/web/root/pages/002-files.ssjs
@@ -33,7 +33,7 @@ if (typeof http_request.query.dir !== 'undefined' &&
 			if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) {
 				description = '<pre class="ansi">' + html_encode(file.extdesc, true, false, true, true) + '</pre>';
 			} else {
-				description = '<pre class="list">' + file.extdesc + '</pre>';
+				description = '<pre class="list">' + file.extdesc.replace(/[^\r,\n,\x20-\x7E]/g, "") + '</pre>';
 			}
 		}
 		writeln(
-- 
GitLab


From 783d235321c694851a3c2b89742189fd1f0127dd Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 17 Dec 2016 23:17:22 -0500
Subject: [PATCH 169/752] If no rlogin/telnet interface specified in sbbs.ini
 [BBS], connect to 127.0.0.1, otherwise connect to specified address.

---
 mods/websocket-rlogin-service.js | 8 +++++++-
 mods/websocket-telnet-service.js | 8 +++++++-
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 0672ea3362..508e7016b8 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -397,8 +397,14 @@ try {
 	var ini = f.iniGetObject('BBS');
 	f.close();
 
+    if (typeof ini.RLoginInterface === 'undefined' || ini.RLoginInterface === '0.0.0.0') {
+        var rlogin_addr = '127.0.0.1';
+    } else {
+        var rlogin_addr = ini.RLoginInterface;
+    }
+
 	rlogin = new RLoginClient(
-		{	host : system.inet_addr,
+		{	host : rlogin_addr,
 			port : ini.RLoginPort,
 			clientUsername : usr.security.password,
 			serverUsername : usr.alias,
diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index 8764855ffc..cdcb50870f 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -228,8 +228,14 @@ try {
     var ini = f.iniGetObject('BBS');
     f.close();
 
+    if (typeof ini.TelnetInterface === 'undefined' || ini.TelnetInterface === '0.0.0.0') {
+        var telnet_addr = '127.0.0.1';
+    } else {
+        var telnet_addr = ini.TelnetInterface;
+    }
+
     var wss = new WebSocketProxy(client);
-    var telnet = new TelnetClient(system.inet_addr, ini.TelnetPort);
+    var telnet = new TelnetClient(telnet_addr, ini.TelnetPort);
  
 	while (client.socket.is_connected && telnet.connected) {
 
-- 
GitLab


From b5b0867376434bf05ab8e0a074e462694905f960 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 19 Dec 2016 15:32:23 -0500
Subject: [PATCH 170/752] Debug logging stuff.

---
 mods/websocket-rlogin-service.js | 1 +
 mods/websocket-telnet-service.js | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 508e7016b8..eff2a797a7 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -403,6 +403,7 @@ try {
         var rlogin_addr = ini.RLoginInterface;
     }
 
+    log(LOG_DEBUG, 'Connecting to ' + rlogin_addr + ':' + ini.RLoginPort);
 	rlogin = new RLoginClient(
 		{	host : rlogin_addr,
 			port : ini.RLoginPort,
diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index cdcb50870f..b5f9327a40 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -235,8 +235,9 @@ try {
     }
 
     var wss = new WebSocketProxy(client);
+    log(LOG_DEBUG, 'Connecting to ' + telnet_addr + ':' + ini.TelnetPort);
     var telnet = new TelnetClient(telnet_addr, ini.TelnetPort);
- 
+
 	while (client.socket.is_connected && telnet.connected) {
 
     	wss.cycle();
-- 
GitLab


From cd1183f134b164cea959db5ad2464048a6ef2bc2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 19 Dec 2016 15:40:12 -0500
Subject: [PATCH 171/752] Make log messages more obvious.

---
 mods/websocket-rlogin-service.js | 2 +-
 mods/websocket-telnet-service.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index eff2a797a7..67fa60e6cb 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -403,7 +403,7 @@ try {
         var rlogin_addr = ini.RLoginInterface;
     }
 
-    log(LOG_DEBUG, 'Connecting to ' + rlogin_addr + ':' + ini.RLoginPort);
+    log(LOG_DEBUG, 'WSRS Connecting to ' + rlogin_addr + ':' + ini.RLoginPort);
 	rlogin = new RLoginClient(
 		{	host : rlogin_addr,
 			port : ini.RLoginPort,
diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index b5f9327a40..7dc6dae15e 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -235,7 +235,7 @@ try {
     }
 
     var wss = new WebSocketProxy(client);
-    log(LOG_DEBUG, 'Connecting to ' + telnet_addr + ':' + ini.TelnetPort);
+    log(LOG_DEBUG, 'WSTS Connecting to ' + telnet_addr + ':' + ini.TelnetPort);
     var telnet = new TelnetClient(telnet_addr, ini.TelnetPort);
 
 	while (client.socket.is_connected && telnet.connected) {
-- 
GitLab


From 084d333bbd459a88cf154938070f2cf2196407b4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 25 Dec 2016 23:30:07 -0500
Subject: [PATCH 172/752] Convert xtrn section code from modopts to lowercase
 for use as key into xtrn_area.sec

---
 web/root/pages/003-games.xjs | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index b54c04fedd..cc5237faaa 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -10,8 +10,8 @@
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 
 <script type="text/javascript">
-	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
+	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
+	fTelnet.Port = 1514;//<?xjs write(webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
@@ -37,6 +37,7 @@
 
 	settings.xtrn_sections.forEach(
 		function (section) {
+			section = section.toLowerCase();
 			writeln('<div class="list-group-item">');
 			writeln('<h4>' + xtrn_area.sec[section].name + '</h4>');
 			writeln('<ul class="nav nav-pills">');
@@ -57,4 +58,4 @@
 
 ?>
 
-</div>
\ No newline at end of file
+</div>
-- 
GitLab


From 63ee6c2915057e9c127dae159476838574ee96c4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 25 Dec 2016 23:32:00 -0500
Subject: [PATCH 173/752] Whoops

---
 web/root/pages/003-games.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index cc5237faaa..f66d2ac1b6 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -11,7 +11,7 @@
 
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
-	fTelnet.Port = 1514;//<?xjs write(webSocketRLogin.Port); ?>;
+	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-- 
GitLab


From e66fbe1b1742e67e203b820989e5ee383a14b2f2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 25 Dec 2016 23:34:00 -0500
Subject: [PATCH 174/752] Whoops

---
 web/root/pages/003-games.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index f66d2ac1b6..bafbb8baef 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -10,7 +10,7 @@
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 
 <script type="text/javascript">
-	fTelnet.Hostname = '<?xjs write(system.inet_addr); ?>';
+	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
 	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
-- 
GitLab


From 61cb4097fb0ff0f3af25d68fc4bca5ab57364bb7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 25 Dec 2016 23:47:13 -0500
Subject: [PATCH 175/752] make 'getSomeMessageHeaders' less stupid (but still
 stupid).

---
 web/lib/forum.js | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index e9e16158a0..32f636202c 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -53,6 +53,31 @@ function listSubs(group) {
     return response;
 }
 
+function listThreads(sub, offset, count) {
+
+    offset = parseInt(offset);
+    if (isNaN(offset) || offset < 0) return false;
+    count = parseInt(count);
+    if (isNaN(count) || count < 1) return false;
+
+    var threads = getMessageThreads(sub, settings.max_messages);
+    if (offset >= threads.order.length) return false;
+
+    var stop = Math.min(threads.order.length, offset + count);
+    var ret = [];
+    for (var n = offset; n < stop; n++) {
+        var thread = threads.thread[threads.order[n]];
+        var msgs = Object.keys(thread.messages);
+        thread.first = thread.messages[msgs[0]];
+        thread.last = thread.messages[msgs[msgs.length - 1]];
+        delete thread.messages;
+        ret.push(thread);
+    }
+
+    return ret;
+
+}
+
 function getSubUnreadCount(sub) {
     var ret = {
         scanned : 0,
@@ -882,16 +907,17 @@ function getMessageThreads(sub, max) {
         }
     }
 
-    function getSomeMessageHeaders(msgBase, start, count) {
-        if (start < 0 || start > msgBase.last_msg) return {};
-        if (start + count - 1 > msgBase.last_msg) {
-            count = msgBase.last_msg - start;
-        }
+    function getSomeMessageHeaders(msgBase, count) {
+        var start = msgBase.last_msg - count;
+        if (start < msgBase.first_msg) start = msgBase.first_msg;
         var headers = {};
-        for (var m = 0; m < count; m++) {
-            var header = msgBase.get_msg_header(start + m);
+        var c = 0;
+        for (var m = start; m < msgBase.last_msg; m++) {
+            var header = msgBase.get_msg_header(m);
             if (header === null || header.attr&MSG_DELETE) continue;
             headers[header.number] = header;
+            c++;
+            if (c >= count) break;
         }
         return headers;
     }
@@ -901,7 +927,7 @@ function getMessageThreads(sub, max) {
     if ((typeof max === 'number' && max > 0) ||
         typeof msgBase.get_all_msg_headers !== 'function'
     ) {
-        var headers = getSomeMessageHeaders(msgBase, 0, max);
+        var headers = getSomeMessageHeaders(msgBase, max);
     } else {
         var headers = msgBase.get_all_msg_headers();
     }
-- 
GitLab


From 502c4b8db935dade4bafaa86ebd4c0a04ccdf6e1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 12:59:19 -0500
Subject: [PATCH 176/752] If rlogin_port is specified in modopts.ini [web], use
 that port; otherwise, use WebSocketRLogin port specified in services.ini

---
 web/root/pages/003-games.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index bafbb8baef..ad712937e3 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -11,7 +11,7 @@
 
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(webSocketRLogin.Port); ?>;
+	fTelnet.Port = <?xjs write(settings.rlogin_port || webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-- 
GitLab


From cee9e426fec69495cb0c8e4abc95c85ec1f119c7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:02:52 -0500
Subject: [PATCH 177/752] Actually, let's call that setting
 websocket_rlogin_port to avoid corn-fusion.

---
 web/root/pages/003-games.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index ad712937e3..beed0eaa1b 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -11,7 +11,7 @@
 
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(settings.rlogin_port || webSocketRLogin.Port); ?>;
+	fTelnet.Port = <?xjs write(settings.webSocketRLogin || websocket_rlogin_port.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-- 
GitLab


From 140782ddb26693c388e45c8a87be030e4fe3dec6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:04:51 -0500
Subject: [PATCH 178/752] Use modopts.ini [web] websocket_telnet_port if
 available, or use WebSocketTelnet port from services.ini

---
 web/root/pages/000-home.xjs | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 5744bffaa8..500dc1034c 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,21 +1,20 @@
 <!--Home-->
 <?xjs 
-	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
+	load('../xtrn/bullshit/bullshit.ssjs');
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 <div class="row">
 	<div class="center-block" style="width:200px;margin-bottom:1em;">
-		<button id="ftelnet-connect" class="btn btn-primary">
-			Connect via Telnet
-		</button>
+		<button id="ftelnet-connect" class="btn btn-primary">Connect via Telnet</button>
 	</div>
 </div>
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write((webSocket === null) ? 1123 : webSocket.Port); ?>;
+	fTelnet.Port = 1124;//<?xjs write((webSocket === null) ? 1123 : webSocket.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
@@ -25,5 +24,18 @@
 		)
 	);
 	fTelnet.Init();
-	$('#ftelnet-connect').click(function () { fTelnet.Connect(); });
+	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
 </script>
+
+<div class="well well-sm">
+	<a title="RSS Feed" href="http://bbs.electronicchicken.com/rss/bullshitrss.xml">
+		<h4>
+			<img alt="" src="./images/rss.png" style="border-width:0;float:left;margin-right:1em;">
+			Bulletins
+		</h4>
+	</a>
+</div>
+
+<?xjs
+	bullshit('bullshit', 5);
+?>
-- 
GitLab


From 90e2bc50ad6162cc2d8d977f261a196bf96e600d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:18:00 -0500
Subject: [PATCH 179/752] Use modopts.ini [web] websocket_telnet_port if
 available, or use WebSocketTelnet port from services.ini

---
 web/root/pages/000-home.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 500dc1034c..0abd67e6c8 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -14,7 +14,7 @@
 </div>
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = 1124;//<?xjs write((webSocket === null) ? 1123 : webSocket.Port); ?>;
+	fTelnet.Port = <?xjs write(settings.websocket_telnet_port : webSocket.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-- 
GitLab


From 3cc8141b581caa1ba50bb2848ed744b6aaec031a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:18:57 -0500
Subject: [PATCH 180/752] Use modopts.ini [web] websocket_telnet_port if
 available, or use WebSocketTelnet port from services.ini

---
 web/root/pages/000-home.xjs | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 0abd67e6c8..9b0b3aea43 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -26,16 +26,3 @@
 	fTelnet.Init();
 	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
 </script>
-
-<div class="well well-sm">
-	<a title="RSS Feed" href="http://bbs.electronicchicken.com/rss/bullshitrss.xml">
-		<h4>
-			<img alt="" src="./images/rss.png" style="border-width:0;float:left;margin-right:1em;">
-			Bulletins
-		</h4>
-	</a>
-</div>
-
-<?xjs
-	bullshit('bullshit', 5);
-?>
-- 
GitLab


From afe25893e6806e7c607a92033ba4f5b3a7028ab1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:19:27 -0500
Subject: [PATCH 181/752] rage

---
 web/root/pages/000-home.xjs | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 9b0b3aea43..2a6de18919 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,9 +1,4 @@
 <!--Home-->
-<?xjs 
-	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
-	load(settings.web_lib + 'ftelnet.js');
-	load('../xtrn/bullshit/bullshit.ssjs');
-?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
-- 
GitLab


From a36a8403c19539cb20c022794fba59f5e5228805 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:21:24 -0500
Subject: [PATCH 182/752] Derp.

---
 web/root/pages/000-home.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 2a6de18919..93fe7b45bb 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -9,7 +9,7 @@
 </div>
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(settings.websocket_telnet_port : webSocket.Port); ?>;
+	fTelnet.Port = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-- 
GitLab


From 92c0df722fbc9c5eae23ea3e3091115bb0684dee Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:24:02 -0500
Subject: [PATCH 183/752] Don't forget to load those ftelnet helpers.

---
 web/root/pages/000-home.xjs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 93fe7b45bb..d3269a2900 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,4 +1,8 @@
 <!--Home-->
+<?xjs
+	if (typeof argv[0] != 'boolean' || !argv[0]) exit(); // I don't remember what this is for
+	load(settings.web_lib + 'ftelnet.js');
+?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
-- 
GitLab


From 465671ecc7f7aec03d49b12b8e961f7084e9de57 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 26 Dec 2016 13:31:31 -0500
Subject: [PATCH 184/752] Overcome dyslexia.

---
 web/root/pages/003-games.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index beed0eaa1b..d9fb9ed94c 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -11,7 +11,7 @@
 
 <script type="text/javascript">
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(settings.webSocketRLogin || websocket_rlogin_port.Port); ?>;
+	fTelnet.Port = <?xjs write(settings.websocket_rlogin_port || webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
 	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
 	fTelnet.StatusBarVisible = false;
-- 
GitLab


From 070538497c84ef0de4f81fee54a48d059a641b4e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 28 Dec 2016 12:38:27 -0500
Subject: [PATCH 185/752] Removed xtrn_sections setting; replaced by inverse
 xtrn_blacklist setting.  Added settings.page_size.

---
 web/lib/init.js | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index bc24315dee..10875a7b5a 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -39,22 +39,10 @@ if (typeof settings.user_registration !== 'boolean') {
 
 }
 
-if (typeof settings.xtrn_sections === 'string') {
-	settings.xtrn_sections = settings.xtrn_sections.split(',').filter(
-		function (section) {
-			if (typeof xtrn_area.sec[section] === 'undefined') return false;
-			if (!xtrn_area.sec[section].can_access) return false;
-			if (xtrn_area.sec_list[
-					xtrn_area.sec[section].index
-				].prog_list.length < 1
-			) {
-				return false;
-			}
-			return true;	
-		}
-	);
-}
-
 if (typeof settings.max_messages !== 'number' || settings.max_messages < 0) {
 	settings.max_messages = 0;
-} 
\ No newline at end of file
+}
+
+if (typeof settings.page_size !== 'number' || settings.page_size < 1) {
+	settings.page_size = 25;
+}
\ No newline at end of file
-- 
GitLab


From d9e9d38e40ad4eff4bdd4ee5025e18644eb8d4d3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 28 Dec 2016 12:40:07 -0500
Subject: [PATCH 186/752] Reinit fTelnet on disconnect.  List all xtrn sections
 and programs whose internal codes are not in xtrn_blacklist.

---
 web/root/pages/003-games.xjs | 94 +++++++++++++++++++++++++-----------
 1 file changed, 66 insertions(+), 28 deletions(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index d9fb9ed94c..00ede7ab11 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -1,7 +1,36 @@
 <!--Games-->
 <?xjs 
+
 	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
+
+	if (typeof settings.xtrn_blacklist === 'string') {
+		settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(',');
+	} else {
+		settings.xtrn_blacklist = [];
+	}
+
+	var xtrn = {};
+	xtrn_area.sec_list.forEach(
+		function (sec) {
+			if (!sec.can_access || sec.prog_list.length < 1) return;
+			if (settings.xtrn_blacklist.indexOf(sec.code.toLowerCase()) > -1) {
+				return;
+			}
+			var x = [];
+			sec.prog_list.forEach(
+				function (prog) {
+					if (!prog.can_access || !prog.can_run) return;
+					if (settings.xtrn_blacklist.indexOf(prog.code.toLowerCase()) > -1) {
+						return;
+					}
+					x.push({ c : prog.code, n : prog.name });
+				}
+			);
+			if (x.length > 0) xtrn[sec.name] = x;
+		}
+	);
+
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
@@ -9,7 +38,19 @@
 <a name="fTelnet"></a>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 
+<div id="xtrn-list" class="list-group">
+	<div id="xtrn-list-template" class="list-group-item" style="display:none;">
+		<h4></h4>
+		<ul class="nav nav-pills">
+			<li id="xtrn-item-template" role="presentation" style="display:none;">
+			<a href="#fTelnet"></a>
+			</li>
+		</ul>
+	</div>
+</div>
+
 <script type="text/javascript">
+
 	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
 	fTelnet.Port = <?xjs write(settings.websocket_rlogin_port || webSocketRLogin.Port); ?>;
 	fTelnet.ConnectionType = 'telnet';
@@ -20,42 +61,39 @@
 			navigator.userAgent
 		)
 	);
-	$(document).ready(fTelnet.Init());
+	function ftInit() {
+		$('#fTelnetContainer').html('');
+		fTelnet.Init();
+		fTelnet.OnConnectionClose = ftInit;
+	}
+	$(document).ready(ftInit);
 	function launchXtrn(code) {
 		$.getJSON(
 			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function(data) {
-				fTelnet.Connect();
-			}
+			function(data) { fTelnet.Connect(); }
 		);
 	}
-</script>
 
-<div class="list-group">
-
-<?xjs
-
-	settings.xtrn_sections.forEach(
-		function (section) {
-			section = section.toLowerCase();
-			writeln('<div class="list-group-item">');
-			writeln('<h4>' + xtrn_area.sec[section].name + '</h4>');
-			writeln('<ul class="nav nav-pills">');
-			xtrn_area.sec_list[xtrn_area.sec[section].index].prog_list.forEach(
-				function (program) {
-					writeln(
-						'<li role="presentation">' +
-						'<a href="#fTelnet" onclick="launchXtrn(\'' +
-							program.code + '\')">' + program.name + '</a>' +
-						'</li>'
-					);
+	var xtrn = <?xjs write(JSON.stringify(xtrn)); ?>;
+	Object.keys(xtrn).forEach(
+		function (x) {
+			var e = $('#xtrn-list-template').clone();
+			$($(e).find('h4')[0]).text(x);
+			var ul = $($(e).find('ul')[0]);
+			xtrn[x].forEach(
+				function (xx) {
+					var li = $('#xtrn-item-template').clone();
+					var a = $(li).find('a')[0];
+					$(a).text(xx.n);
+					$(a).click(function () { launchXtrn(xx.c); });
+					$(ul).append(li);
+					$(li).show();
 				}
 			);
-			writeln('</ul>');
-			writeln('</div>');
+			$(e).append(ul);
+			$('#xtrn-list').append(e);
+			$(e).show();
 		}
 	);
 
-?>
-
-</div>
+</script>
-- 
GitLab


From 84478af9c40204d48b1ef51addf1116156180aa7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 28 Dec 2016 12:41:46 -0500
Subject: [PATCH 187/752] Monitor websocket -> rlogin server connection, set
 state.connected to false as needed.

---
 mods/websocket-rlogin-service.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 67fa60e6cb..e3285e9cda 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -337,6 +337,11 @@ var RLoginClient = function(options) {
 
 	this.cycle = function () {
 
+		if (!handle.is_connected) {
+			state.connected = false;
+			return;
+		}
+
 		getServerData();
 
 		if (state.suspendInput || clientBuffer.length < 1) return;
@@ -403,7 +408,6 @@ try {
         var rlogin_addr = ini.RLoginInterface;
     }
 
-    log(LOG_DEBUG, 'WSRS Connecting to ' + rlogin_addr + ':' + ini.RLoginPort);
 	rlogin = new RLoginClient(
 		{	host : rlogin_addr,
 			port : ini.RLoginPort,
-- 
GitLab


From 33a49bf47c847268068844f8d6fbbc161e168a8c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 28 Dec 2016 12:51:28 -0500
Subject: [PATCH 188/752] Removed xtrn_sections config setting; notes on
 xtrn_blacklist added.

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 55b7e6bc6c..cdfde53eba 100644
--- a/README.md
+++ b/README.md
@@ -38,8 +38,6 @@ A web interface for Synchronet BBS
 	minimum_password_length = 6
 	; Limit the length of a telegram (in characters) that a web user can send
 	maximum_telegram_length = 800
-	; Which external program sections to list on the Games page (comma-separated)
-	xtrn_sections = games,puzzle,rpg,erotic
 	; Where (absolute or relative to 'exec') the 'lib' and 'root' directories live
 	web_directory = ../web
 	; Path to a .ans file to use as the ftelnet splash screen
@@ -53,6 +51,8 @@ A web interface for Synchronet BBS
 	vote_functions = true
 	; Refresh nodelist, vote counts, etc. this often (in milliseconds)
 	refresh_interval = 60000
+	; External Programs (or entire sections) to exclude from the Games page
+	xtrn_blacklist = scfg,oneliner
 ```
 - Add the following section to your *ctrl/services.ini* file:
 ```ini
@@ -100,7 +100,7 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 ###Configuration
 
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
-- Look at those *xtrn_sections* in the [web] section of *ctrl/modopts.ini*.  They should be the *internal codes* of all External Programs sections that you want to make available to authenticated users via the web.  (You probably don't have an *erotic* External Programs area, but if you do ... that's cool.)
+- Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
 
 ###Customization
 
-- 
GitLab


From 89c98cea9f1846d26b01c4eaac32d72e51209315 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 29 Dec 2016 13:31:16 -0500
Subject: [PATCH 189/752] Send client back to / on logout.

---
 web/root/js/common.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index 8806b7ce31..338b9ef46f 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -38,9 +38,7 @@ function logout() {
 			}
 		}
 	).done(
-		function (data) {
-			if (!data.authenticated) window.location.reload(true);
-		}
+		function (data) { if (!data.authenticated) window.location.href = '/'; }
 	);
 }
 
-- 
GitLab


From fd6a3402bf3db8f6dd67b63cc8059c6f22d7d07f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Jan 2017 15:07:20 -0500
Subject: [PATCH 190/752] Less restrictive cleanParam.

---
 web/root/api/register.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 3bd2e5d231..9b57566f9a 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -37,7 +37,7 @@ function required(mask) {
 
 function cleanParam(param) {
 	if (paramExists(param)) {
-		return http_request.query[param][0].replace(/[^\x20-\x7E]/g, '');
+		return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
 	}
 	return "";
 }
-- 
GitLab


From 5560d2745950cbc918b74a5c13ef6db4f75a4194 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Jan 2017 15:24:51 -0500
Subject: [PATCH 191/752] Added forum_extended_ascii setting, default true. 
 Swedish people may wish to set this to false.

---
 web/lib/init.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/web/lib/init.js b/web/lib/init.js
index 10875a7b5a..27e1aea82a 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -45,4 +45,8 @@ if (typeof settings.max_messages !== 'number' || settings.max_messages < 0) {
 
 if (typeof settings.page_size !== 'number' || settings.page_size < 1) {
 	settings.page_size = 25;
+}
+
+if (typeof settings.forum_extended_ascii !== 'boolean') {
+	settings.forum_extended_ascii = true;
 }
\ No newline at end of file
-- 
GitLab


From 8a4611ff040e1a115ea2ea37d804547203e8c1d7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Jan 2017 15:26:14 -0500
Subject: [PATCH 192/752] formatMessage accepts an exascii argument, whether to
 tell html_encode to escape extended ASCII.

---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 32f636202c..da1b292f42 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -775,7 +775,7 @@ function quotify(body) {
 }
 
 // Format message body for the web
-function formatMessage(body, ansi) {
+function formatMessage(body, ansi, exascii) {
 
     // Workaround for html_encode(body, true, false, false, false);
     // which causes a crash if body is empty
@@ -820,7 +820,7 @@ function formatMessage(body, ansi) {
 
         // Format for the web
         body = word_wrap(body, body.length);
-        body = html_encode(body, true, false, false, false);
+        body = html_encode(body, exascii, false, false, false);
         body = quotify(body);
         body = linkify(body);
         body = body.replace(/\r\n$/,'');
-- 
GitLab


From fb99a2b2a3b6b8c9edee7476ab938062d6115dbc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Jan 2017 15:26:54 -0500
Subject: [PATCH 193/752] Pass ansi (false) and exascii
 (settings.forum_extended_ascii) arguments to formatMessage.

---
 web/root/pages/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index c1d4427a46..8db8ed2c91 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -320,7 +320,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		// This is a normal message
 		} else {
-			writeln(formatMessage(body));
+			writeln(formatMessage(body, false, settings.forum_extended_ascii));
 			writeln(strings.message.body.close);
 		}
 
-- 
GitLab


From 831329954a33d22cdde56ef63ef738a963590769 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Jan 2017 15:31:24 -0500
Subject: [PATCH 194/752] Added forum_extended_ascii to example modopts.ini
 section.

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index cdfde53eba..1163d0af0e 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,8 @@ A web interface for Synchronet BBS
 	refresh_interval = 60000
 	; External Programs (or entire sections) to exclude from the Games page
 	xtrn_blacklist = scfg,oneliner
+	; Use extended-ASCII characters when displaying forum messages
+	forum_extended_ascii = true
 ```
 - Add the following section to your *ctrl/services.ini* file:
 ```ini
-- 
GitLab


From d00ed156751e35615b5c2c328c82c3ba4ca3e084 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 08:50:43 -0500
Subject: [PATCH 195/752] Fixed bug in populating new user 'real name'.

---
 web/root/api/register.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 9b57566f9a..230d1c7b41 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -21,7 +21,7 @@ var reply = {
 var prepUser = {
 	alias : '',
 	handle : '',
-	realname : '',
+	name : '',
 	netmail : '',
 	address : '',
 	location : '',
@@ -152,7 +152,7 @@ if (required(UQ_REALNAME) &&
 ) {
 	reply.errors.push('Valid real name is required.');
 } else {
-	prepUser.realname = cleanParam('realname');
+	prepUser.name = cleanParam('realname');
 }
 
 if (required(UQ_LOCATION) &&
-- 
GitLab


From 180d8e386b50c56a999e3841501f88437f92fe4d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 09:54:50 -0500
Subject: [PATCH 196/752] Fixed bug re: storing user's gender.

---
 web/root/api/register.ssjs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 230d1c7b41..9f9ec26b86 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -189,11 +189,14 @@ if (required(UQ_PHONE) &&
 }
 
 if (required(UQ_SEX) &&
-	(!paramExists('gender') || paramLength('gender') != 1)
+	(	!paramExists('gender') ||
+		paramLength('gender') != 1 ||
+		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
+	)
 ) {
 	reply.errors.push('Sex is required. Heh heh.');
 } else {
-	prepUser.gender = cleanParam('gender');
+	prepUser.gender = http_request.query.gender[0];
 }
 
 if (paramExists('birth') &&
-- 
GitLab


From 77e55b19bbd3d117994ce60f75d0448e72167c34 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 09:55:26 -0500
Subject: [PATCH 197/752] Fixed gender radio input thingie; removed some
 unnecessary inline xjs.

---
 web/root/pages/000-register.xjs | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
index 7613ea110e..b96dc1aa78 100644
--- a/web/root/pages/000-register.xjs
+++ b/web/root/pages/000-register.xjs
@@ -142,40 +142,33 @@
 			<?xjs iconRequired(UQ_SEX); ?> <label>Gender</label>&nbsp;
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-withheld" name="gender-withheld" checked> Withheld 
+					<input type="radio" id="gender-withheld" name="gender" value="X" checked> Withheld 
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-male" name="gender-male"> Male 
+					<input type="radio" id="gender-male" name="gender" value="M"> Male 
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-female" name="gender-female"> Female
+					<input type="radio" id="gender-female" name="gender" value="F"> Female
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-other" name="gender-other"> Other 
+					<input type="radio" id="gender-other" name="gender" value="O"> Other 
 				</label>
 			</div>
 		</div>
 		<br>
 
-		<?xjs
-			if(system.newuser_password !== '') {
-				writeln('<div class="form-group">');
-				writeln(
-					'<label for="newuser-password">Registration password</label>'
-				);
-				writeln(
-					'<input type="password" id="newuser-password" ' +
-					'name="newuser-password" data-minlength="1" maxlength="8">'
-				);
-				writeln('</div>');
-			}
-		?>
+		<?xjs if(system.newuser_password !== '') { ?>
+		<div class="form-group">
+			<label for="newuser-password">Registration password</label>
+			<input type="password" id="newuser-password" name="newuser-password" data-minlength="1" maxlength="8">
+		</div>
+		<?xjs } ?>
 
 		<!-- Traps - leave these here. They will be hidden from users. -->
 		<input id="send-me-free-stuff" title="Don't fill this field" name="send-me-free-stuff" type="text">
-- 
GitLab


From 92facbccf2dad0e392d090776c9994553f48ebd6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:13:55 -0500
Subject: [PATCH 198/752] Testing

---
 web/root/api/github.ssjs | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 web/root/api/github.ssjs

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
new file mode 100644
index 0000000000..a2da901056
--- /dev/null
+++ b/web/root/api/github.ssjs
@@ -0,0 +1,4 @@
+var f = new File(system.ctrl_dir + '../github.txt');
+f.open('w');
+f.write(JSON.stringify(http_request));
+f.close();
\ No newline at end of file
-- 
GitLab


From 0068d9f453b177b9adeda395934aa35b0fe78b5d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:20:50 -0500
Subject: [PATCH 199/752] Testing

---
 web/root/api/github.ssjs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index a2da901056..f6cb12486d 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,4 +1,5 @@
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.write(JSON.stringify(http_request));
+f.write(JSON.stringify(http_request.header));
+//f.write(JSON.stringify(http_request));
 f.close();
\ No newline at end of file
-- 
GitLab


From eb1a9a2da9f64a1856d4c8b30c1310d98bf02bd4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:26:58 -0500
Subject: [PATCH 200/752] Testing

---
 web/root/api/github.ssjs | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index f6cb12486d..fa2e9275e9 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,5 +1,14 @@
+load(system.exec_dir + '../web/lib/init.js');
+
+var cc = new CryptContext('HMAC-SHA1');
+cc.set_key(settings.github_secret);
+var secret = cc.decrypt(http_request.header['x-hub-signature'].split('=')[1]);
+
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.write(JSON.stringify(http_request.header));
+//f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
+f.writeln('Secret: ' + settings.github_secret);
+f.writeln('Received: ' + http_request.header['x-hub-signature']);
+f.writeln('Decrypted: ' + secret);
 f.close();
\ No newline at end of file
-- 
GitLab


From 3edd672a7b7886dadda6c15bbf5fee12b83c7ffa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:28:19 -0500
Subject: [PATCH 201/752] Testing

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index fa2e9275e9..643103d4b2 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,6 +1,6 @@
 load(system.exec_dir + '../web/lib/init.js');
 
-var cc = new CryptContext('HMAC-SHA1');
+var cc = new CryptContext(CryptContext.ALGO['HMAC-SHA1']);
 cc.set_key(settings.github_secret);
 var secret = cc.decrypt(http_request.header['x-hub-signature'].split('=')[1]);
 
-- 
GitLab


From 9ff0fd56914dde332ca36e247081bd3283c30251 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:38:33 -0500
Subject: [PATCH 202/752] Testing

---
 web/root/api/github.ssjs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 643103d4b2..199f21a8f5 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -2,13 +2,13 @@ load(system.exec_dir + '../web/lib/init.js');
 
 var cc = new CryptContext(CryptContext.ALGO['HMAC-SHA1']);
 cc.set_key(settings.github_secret);
-var secret = cc.decrypt(http_request.header['x-hub-signature'].split('=')[1]);
+cc.encrypt(JSON.stringify(http_request.query));
+var barf = cc.hashvalue;
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
-f.writeln('Secret: ' + settings.github_secret);
-f.writeln('Received: ' + http_request.header['x-hub-signature']);
-f.writeln('Decrypted: ' + secret);
+f.writeln(barf);
+f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 8254ac51eeb75d504c16e852d46048b140e056c0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:39:44 -0500
Subject: [PATCH 203/752] Testing

---
 web/root/api/github.ssjs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 199f21a8f5..eaadde2356 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -9,6 +9,7 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
+f.writeln(JSON.stringify(http_request.query));
 f.writeln(barf);
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 7ff0c4cd39f7f3b6bcef9479049e4b856764cf98 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:44:55 -0500
Subject: [PATCH 204/752] Testing

---
 web/root/api/github.ssjs | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index eaadde2356..a9b68f9af3 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,15 +1,9 @@
 load(system.exec_dir + '../web/lib/init.js');
 
-var cc = new CryptContext(CryptContext.ALGO['HMAC-SHA1']);
-cc.set_key(settings.github_secret);
-cc.encrypt(JSON.stringify(http_request.query));
-var barf = cc.hashvalue;
-
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
-f.writeln(JSON.stringify(http_request.query));
-f.writeln(barf);
+f.writeln(http_request.query);
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From ae54ef729017915704a5d1388d0dbf6ed1c31cfd Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:45:57 -0500
Subject: [PATCH 205/752] Testing

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index a9b68f9af3..f417bba0bd 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -4,6 +4,6 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
-f.writeln(http_request.query);
+f.writeln(JSON.stringify(Object.keys(http_request.query)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 6284f01387f5864b76fe7a5abba0abc0b30483b1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:46:53 -0500
Subject: [PATCH 206/752] Testing

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index f417bba0bd..20309504f2 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -4,6 +4,6 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
-f.writeln(JSON.stringify(Object.keys(http_request.query)));
+f.writeln(JSON.stringify(Object.keys(http_request)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From ad9ce4fc5b843420a313798cd475a46076549f54 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:48:45 -0500
Subject: [PATCH 207/752] Testing

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 20309504f2..2f84337f86 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -4,6 +4,6 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
-f.writeln(JSON.stringify(Object.keys(http_request)));
+f.writeln(JSON.stringify(http_request.post_data));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From de1f9204cad6691aedba99179e6fa70aa4e63870 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:49:21 -0500
Subject: [PATCH 208/752] Testing

---
 web/root/api/github.ssjs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 2f84337f86..7fa4109412 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -5,5 +5,6 @@ f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
 f.writeln(JSON.stringify(http_request.post_data));
+f.writeln(JSON.stringify(http_request.query));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From a3549de458c4f38e91b8caf179d9645ec7bdcd8f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:50:05 -0500
Subject: [PATCH 209/752] Testing

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 7fa4109412..de0bb5203f 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -5,6 +5,6 @@ f.open('w');
 //f.write(JSON.stringify(http_request.header));
 //f.write(JSON.stringify(http_request));
 f.writeln(JSON.stringify(http_request.post_data));
-f.writeln(JSON.stringify(http_request.query));
+f.writeln(http_request.query.payload[0]);
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From a5ed7717ade78d858c580e8cfb787e7930b5ee1c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:54:40 -0500
Subject: [PATCH 210/752] Testing

---
 web/root/api/github.ssjs | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index de0bb5203f..a0ed870978 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,10 +1,15 @@
 load(system.exec_dir + '../web/lib/init.js');
+load('sha1.js');
+
+var payload = http_request.query.payload[0];
+
+var cc = new CryptContext(CryptContext.ALGO['HMAC-SHA1']);
+cc.set_key(settings.github_secret);
+var pe = cc.encrypt(payload);
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-//f.write(JSON.stringify(http_request.header));
-//f.write(JSON.stringify(http_request));
-f.writeln(JSON.stringify(http_request.post_data));
-f.writeln(http_request.query.payload[0]);
+f.writeln(Sha1.hash(pe));
+f.writeln(cc.encrypt(Sha1.hash(payload)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 3cbbdd696126e00e2b204d700ff9549df0f99d70 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 10:55:40 -0500
Subject: [PATCH 211/752] Testing

---
 web/root/api/github.ssjs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index a0ed870978..850dc8485f 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -5,11 +5,10 @@ var payload = http_request.query.payload[0];
 
 var cc = new CryptContext(CryptContext.ALGO['HMAC-SHA1']);
 cc.set_key(settings.github_secret);
-var pe = cc.encrypt(payload);
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.writeln(Sha1.hash(pe));
+f.writeln(cc.encrypt(payload));
 f.writeln(cc.encrypt(Sha1.hash(payload)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From d0b7eebe82ed12a96885556031670bbd47bf0ac5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:01:37 -0500
Subject: [PATCH 212/752] Testing

---
 web/root/api/github.ssjs | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 850dc8485f..3ecf20d7e7 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,14 +1,10 @@
 load(system.exec_dir + '../web/lib/init.js');
-load('sha1.js');
+load('hmac.js');
 
 var payload = http_request.query.payload[0];
 
-var cc = new CryptContext(CryptContext.ALGO['HMAC-SHA1']);
-cc.set_key(settings.github_secret);
-
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.writeln(cc.encrypt(payload));
-f.writeln(cc.encrypt(Sha1.hash(payload)));
+f.writeln(hmac_sha1(settings.github_secret, payload));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From aabdb695b3eb517893cf7c2a7666ca2739906714 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:03:48 -0500
Subject: [PATCH 213/752] Testing

---
 web/root/api/github.ssjs | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 3ecf20d7e7..3a456c485a 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,10 +1,20 @@
 load(system.exec_dir + '../web/lib/init.js');
 load('hmac.js');
 
+function b2h(str) {
+    if (typeof str !== 'string') str += '';
+    var ret = '';
+    for (var i = 0; i < str.length; i++) {
+            var n = ascii(str[i]).toString(16);
+            ret += n.length < 2 ? '0' + n : n;
+    }
+    return ret;
+}
+
 var payload = http_request.query.payload[0];
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.writeln(hmac_sha1(settings.github_secret, payload));
+f.writeln(b2h(hmac_sha1(settings.github_secret, payload)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 24029dd6999f51ec9bb133d4b85c92e48f1c2f2d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:09:51 -0500
Subject: [PATCH 214/752] Testing

---
 web/root/api/github.ssjs | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 3a456c485a..0108af35b3 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,12 +1,18 @@
 load(system.exec_dir + '../web/lib/init.js');
 load('hmac.js');
 
+function a2h(str) {
+    var hex = '';
+    str.split('').forEach(function(ch) { hex += parseInt(ascii(ch), 16); });
+    return hex;
+}
+
 function b2h(str) {
     if (typeof str !== 'string') str += '';
     var ret = '';
     for (var i = 0; i < str.length; i++) {
-            var n = ascii(str[i]).toString(16);
-            ret += n.length < 2 ? '0' + n : n;
+        var n = ascii(str[i]).toString(16);
+        ret += n.length < 2 ? '0' + n : n;
     }
     return ret;
 }
@@ -15,6 +21,6 @@ var payload = http_request.query.payload[0];
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.writeln(b2h(hmac_sha1(settings.github_secret, payload)));
+f.writeln(b2h(hmac_sha1(a2h(settings.github_secret), payload)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 8ba2dd69a63a76f53f1753bc09c36ab7ff8429c9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:14:20 -0500
Subject: [PATCH 215/752] Testing

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 0108af35b3..a8e39067c9 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -21,6 +21,6 @@ var payload = http_request.query.payload[0];
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.writeln(b2h(hmac_sha1(a2h(settings.github_secret), payload)));
+f.writeln(b2h(hmac_sha1(settings.github_secret, a2h(payload))));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From 1c241b7c469b98617ed2b80beb79728c75d23621 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:14:52 -0500
Subject: [PATCH 216/752] Testing

---
 web/root/api/github.ssjs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index a8e39067c9..09ada0e603 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -22,5 +22,6 @@ var payload = http_request.query.payload[0];
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 f.writeln(b2h(hmac_sha1(settings.github_secret, a2h(payload))));
+f.writeln(b2h(hmac_sha1(settings.github_secret, payload)));
 f.writeln(http_request.header['x-hub-signature']);
 f.close();
\ No newline at end of file
-- 
GitLab


From d27f281eb6fde897e546013f6b23b189188c6bea Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:16:45 -0500
Subject: [PATCH 217/752] Testing

---
 web/root/api/github.ssjs | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 09ada0e603..22c79fedcc 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -7,6 +7,14 @@ function a2h(str) {
     return hex;
 }
 
+function h2a(hex) {
+	var str = '';
+	for (var i = 0; i < hex.length; i += 2) {
+		str += ascii(parseInt(hex.substr(i, 2), 16));
+	}
+	return str;
+}
+
 function b2h(str) {
     if (typeof str !== 'string') str += '';
     var ret = '';
@@ -23,5 +31,6 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 f.writeln(b2h(hmac_sha1(settings.github_secret, a2h(payload))));
 f.writeln(b2h(hmac_sha1(settings.github_secret, payload)));
-f.writeln(http_request.header['x-hub-signature']);
+f.writeln(h2a(b2h(hmac_sha1(settings.github_secret, payload))));
+f.writeln(http_request.header['x-hub-signature'].split('=')[1]);
 f.close();
\ No newline at end of file
-- 
GitLab


From b419e96031fc10717c97b35f9b6829586e644d8d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:19:23 -0500
Subject: [PATCH 218/752] Testing

---
 web/root/api/github.ssjs | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 22c79fedcc..fd1732084d 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -2,9 +2,7 @@ load(system.exec_dir + '../web/lib/init.js');
 load('hmac.js');
 
 function a2h(str) {
-    var hex = '';
-    str.split('').forEach(function(ch) { hex += parseInt(ascii(ch), 16); });
-    return hex;
+	return str.split('').map(function(ch) { return parseInt(ascii(ch), 16); }).join('');
 }
 
 function h2a(hex) {
@@ -31,6 +29,5 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 f.writeln(b2h(hmac_sha1(settings.github_secret, a2h(payload))));
 f.writeln(b2h(hmac_sha1(settings.github_secret, payload)));
-f.writeln(h2a(b2h(hmac_sha1(settings.github_secret, payload))));
 f.writeln(http_request.header['x-hub-signature'].split('=')[1]);
 f.close();
\ No newline at end of file
-- 
GitLab


From c526aa532f778e7e168ec23f835619e33979aa7b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:20:42 -0500
Subject: [PATCH 219/752] Testing

---
 web/root/api/github.ssjs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index fd1732084d..0b7e5a1e37 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -27,7 +27,6 @@ var payload = http_request.query.payload[0];
 
 var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
-f.writeln(b2h(hmac_sha1(settings.github_secret, a2h(payload))));
-f.writeln(b2h(hmac_sha1(settings.github_secret, payload)));
 f.writeln(http_request.header['x-hub-signature'].split('=')[1]);
+f.writeln(payload);
 f.close();
\ No newline at end of file
-- 
GitLab


From 88e71c9283787a617fcc791ad505ddc1a0c9661e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 11:32:00 -0500
Subject: [PATCH 220/752] Testing

---
 web/root/api/github.ssjs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 0b7e5a1e37..1f39af50d2 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -29,4 +29,5 @@ var f = new File(system.ctrl_dir + '../github.txt');
 f.open('w');
 f.writeln(http_request.header['x-hub-signature'].split('=')[1]);
 f.writeln(payload);
+f.writeln(':D');
 f.close();
\ No newline at end of file
-- 
GitLab


From cc590c2d9a66b8cda611eef6d4194c72a6f4fa11 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 15:09:10 -0500
Subject: [PATCH 221/752] A GitHub webhook script; post a message to a list of
 sub-boards when changes are pushed to a repository.

---
 web/root/api/github.ssjs | 104 ++++++++++++++++++++++++++++++---------
 1 file changed, 80 insertions(+), 24 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 1f39af50d2..0bbf5f4ac0 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,33 +1,89 @@
-load(system.exec_dir + '../web/lib/init.js');
+/*	Posts a notification message to a list of sub-boards when commits are made
+	to a GitHub repository.
+
+	- In your repository, go to Settings -> Webhooks -> Add webhook
+	- Payload URL: https://your-bbs-hostname/api/github.ssjs
+	- Content-type: application/json
+	- Secret: some-secret-passphrase-here
+	- Which events: Just the push event
+	- Active: checked
+	- Add webhook
+
+	- On your BBS server, edit ctrl/modopts.ini
+	- Add a [github-notify] section
+	- For each repository you wish to configure, add a key-value pair like:
+	- my-repository-name=secret,SUB
+	- Where 'secret' is the secret passphrase as configured above
+	- Where 'SUB' is the internal code of a sub-board to post to
+	- Multiple subs can be specified, separated by commas
+*/
+
+load('sbbsdefs.js');
 load('hmac.js');
+var options = load({}, 'modopts.js', 'github_notify');
+
+load(system.exec_dir + '../web/lib/init.js');
+
+function b2h(str) {
+	return str.split('').map(
+		function (e) {
+			var n = ascii(e).toString(16);
+			return n.length < 2 ? ('0' + n) : n;
+		}
+	).join('');
+}
 
-function a2h(str) {
-	return str.split('').map(function(ch) { return parseInt(ascii(ch), 16); }).join('');
+function verify_signature(key, payload, hash) {
+	return (b2h(hmac_sha1(key, payload)) === hash);
 }
 
-function h2a(hex) {
-	var str = '';
-	for (var i = 0; i < hex.length; i += 2) {
-		str += ascii(parseInt(hex.substr(i, 2), 16));
+try {
+	var hash = http_request.header['x-hub-signature'].split('=')[1];
+	var payload = JSON.parse(http_request.post_data);
+	if (typeof options[payload.repository.name] === 'undefined') {
+		throw 'Unknown repository ' + payload.repository.name;
 	}
-	return str;
+	var subs = options[payload.repository.name].split(',');
+	var secret = subs.shift();
+	if (!verify_signature(secret, http_request.post_data, hash)) {
+		throw 'GitHub signature mismatch';
+	}
+} catch (err) {
+	log(LOG_ERR, err);
+	exit();
 }
 
-function b2h(str) {
-    if (typeof str !== 'string') str += '';
-    var ret = '';
-    for (var i = 0; i < str.length; i++) {
-        var n = ascii(str[i]).toString(16);
-        ret += n.length < 2 ? '0' + n : n;
-    }
-    return ret;
-}
+var header = {
+	from : payload.head_commit.author.username,
+	to : 'All',
+	subject : 'Changes to ' + payload.repository.full_name
+};
 
-var payload = http_request.query.payload[0];
+var body = payload.commits.map(
+	function (e) {
+		var ret = [
+			'Commit ID: ' + e.id, 
+			'Author: ' + e.author.username,
+		];
+		if (e.added.length > 0) ret.push('Added: ' + e.added.join(', '));
+		if (e.removed.length > 0) ret.push('Removed: ' + e.removed.join(', '));
+		if (e.modified.length > 0) ret.push('Modified: ' + e.modified.join(', '));
+		ret.push('', 'Message:', e.message, '');
+		return ret.join('\r\n');
+	}
+);
+body.push('Repository URL: ' + payload.repository.url);
+body = body.join('\r\n\r\n');
 
-var f = new File(system.ctrl_dir + '../github.txt');
-f.open('w');
-f.writeln(http_request.header['x-hub-signature'].split('=')[1]);
-f.writeln(payload);
-f.writeln(':D');
-f.close();
\ No newline at end of file
+subs.forEach(
+	function (sub) {
+		try {
+			var mb = new MsgBase(sub);
+			if (!mb.open()) throw 'Failed to open message base ' + sub;
+			if (!mb.save_msg(header, body)) throw 'Failed to save message';
+			mb.close();
+		} catch (err) {
+			log(LOG_ERR, err);
+		}
+	}
+);
\ No newline at end of file
-- 
GitLab


From 9b87a53e4fd871d1afbefcd4ad05f3cc967ffb49 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 15:33:35 -0500
Subject: [PATCH 222/752] Added 'Postal Code' form field, required if street
 address is required.

---
 web/root/api/register.ssjs      | 9 +++++++--
 web/root/pages/000-register.xjs | 9 +++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 9f9ec26b86..6fc60d1016 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -24,6 +24,7 @@ var prepUser = {
 	name : '',
 	netmail : '',
 	address : '',
+	zipcode : '',
 	location : '',
 	phone : '',
 	birthdate : '',
@@ -169,12 +170,16 @@ if (required(UQ_LOCATION) &&
 if (required(UQ_ADDRESS) &&
 	(	!paramExists('address') ||
 		paramLength('address') < MIN_ADDRESS ||
-		paramLength('address') > LEN_ADDRESS
+		paramLength('address') > LEN_ADDRESS ||
+		!paramExists('zipcode') ||
+		paramLength('zipcode') < 3 ||
+		paramLength('zipcode') > LEN_ADDRESS
 	)
 ) {
-	reply.errors.push('Valid street address is required.');
+	reply.errors.push('Valid street address and postal code are required.');
 } else {
 	prepUser.address = cleanParam('address');
+	prepUser.zipcode = cleanParam('zipcode');
 }
 
 if (required(UQ_PHONE) &&
diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
index b96dc1aa78..11b031f169 100644
--- a/web/root/pages/000-register.xjs
+++ b/web/root/pages/000-register.xjs
@@ -112,6 +112,15 @@
 			</span>
 		</div>
 
+		<div class="form-group">
+			<label for="zipcode">Postal Code</label> 
+			<?xjs iconRequired(UQ_ADDRESS); ?>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="zipcode" name="zipcode" placeholder="10101"<?xjs write(required(UQ_ADDRESS)); ?>>
+			<span class="help-block">
+				Maximum of <?xjs write(LEN_ADDRESS); ?> characters
+			</span>
+		</div>
+
 		<div class="form-group">
 			<label for="location">Location</label> 
 			<?xjs iconRequired(UQ_LOCATION); ?>
-- 
GitLab


From 072bae51f9cdd223dcbec76b00806368b885cdb0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 23:09:34 -0500
Subject: [PATCH 223/752] If web/root/custom.css exists, link it.

---
 web/root/index.xjs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index c78aa17ba2..0c61522d46 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -166,6 +166,9 @@
 		<link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
 		<link href="./css/offcanvas.css" rel="stylesheet">
 		<link href="./css/style.css" rel="stylesheet">
+		<?xjs if (file_exists(settings.web_root + 'css/custom.css')) { ?>
+		<link href="./css/custom.css" rel="stylesheet">
+		<?xjs } ?>
 	</head>
 
 	<body>
-- 
GitLab


From 1293dd8fee729c32d9db14a497db39b1833027c8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 23:11:18 -0500
Subject: [PATCH 224/752] Replaced reference to web/root/css/style.css with
 web/root/css/custom.css.  This is the place to put local CSS customizations
 so they won't be clobbered by updates.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 1163d0af0e..b10340729f 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 ###Customization
 
 - This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
-	- You can place your own CSS overrides in *web/root/css/style.css*
+	- You can place your own CSS overrides in *web/root/css/custom.css*
 	- You can load another stylesheet of your own choosing in the &lt;head&gt; section of *web/root/index.xjs* (load it after the others)
 - The sidebar module & page structure is *mostly* similar to the system used in ecWeb v3.  See [the old instructions](http://wiki.synchro.net/howto:ecweb#the_sidebar) for info on adding content.
 	- You can force a link to a page to be placed in the *More* menu by using an underscore as a separator in its filename rather than a hyphen
-- 
GitLab


From 435db6507e32fe4888639d65e66850f18ae66a68 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 6 Jan 2017 23:43:38 -0500
Subject: [PATCH 225/752] Moved some things around and made note of a couple of
 previously undocumented settings.  Added a warning about where you should
 really not extract the sbbs_run archive.

---
 README.md | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index b10340729f..0fff57e395 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ A web interface for Synchronet BBS
 
 - This web interface has been tested with Synchronet BBS 3.17a.  It will probably work with later versions.
 - In addition to updating your Synchronet build, be sure to update your the javascript modules in your *exec* and *exec/load* directories as well.  You can get them from [the Synchronet CVS repository](http://cvs.synchro.net/) or in the [sbbs_run.zip archive](http://vert.synchro.net/Synchronet/sbbs_run.zip).
+	- Do *not* simply unzip sbbs_run.zip overtop your existing sbbs directory structure.  This may overwrite much of your local configuration.  Extract the archive elsewhere and copy things over piecemeal as needed.
 
 ###Quick start
 
@@ -42,9 +43,6 @@ A web interface for Synchronet BBS
 	web_directory = ../web
 	; Path to a .ans file to use as the ftelnet splash screen
 	ftelnet_splash = ../text/synch.ans
-	; Only load this many messages from each sub (default: 0 for all)
-	; (If you get 'Out of memory' errors when viewing subs, tweak this setting)
-	max_messages = 0
 	; Enable or disable keyboard navigation in message threads
 	keyboard_navigation = false
 	; Display upvote/downvote buttons in message threads (3.17)
@@ -53,8 +51,6 @@ A web interface for Synchronet BBS
 	refresh_interval = 60000
 	; External Programs (or entire sections) to exclude from the Games page
 	xtrn_blacklist = scfg,oneliner
-	; Use extended-ASCII characters when displaying forum messages
-	forum_extended_ascii = true
 ```
 - Add the following section to your *ctrl/services.ini* file:
 ```ini
@@ -104,6 +100,24 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
 - Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
 
+####Additional / advanced settings
+
+- The following *optional* settings can be added to the [web] section of your *ctrl/modopts.ini* file:
+
+```ini
+	; Use extended-ASCII characters when displaying forum messages (default: true)
+	forum_extended_ascii = false
+	; Only load this many messages from each sub (default: 0 for all)
+	max_messages = 500
+	; Connect to the websocket proxies on ports other than specified in ctrl/services.ini
+	websocket_telnet_port = 1124
+	websocket_rlogin_port = 1514
+```
+
+- Setting *forum_extended_ascii* to *false* may resolve problems with displaying UTF-8 encoded characters; character codes > 127 will not be assumed to be extended-ASCII references to CP437 characters.
+- Tweaking *max_messages* may improve forum performance or resolve 'Out of memory' errors, if you see any of those when browsing the forum
+- Normally, fTelnet will try to connect to the websocket proxies based on information from *ctrl/services.ini*.  In some situations (eg. when using an HTTPS reverse proxy) it may be necessary to connect to another port unknown to Synchronet.
+
 ###Customization
 
 - This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
-- 
GitLab


From 99e816ad2fbe616489511ea4ca1ec81da976ee27 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Jan 2017 11:45:15 -0500
Subject: [PATCH 226/752] Set overflow:hidden and word-wrap:break word in
 .list-group-item.sidebar, should fix some ugly overflow in browser windows <
 1000px wide.

---
 web/root/css/style.css | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 6b8f41f52d..0cbb2a8d14 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -76,6 +76,11 @@ pre.list {
 	border : none;
 }
 
+.list-group-item.sidebar {
+	overflow : hidden;
+	word-wrap : break-word;
+}
+
 .icon {
 	margin: 0 1em 1em 0;
 }
-- 
GitLab


From dd1431af384e3002982db8a8295f04a588f2bf9b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Jan 2017 15:35:20 -0500
Subject: [PATCH 227/752] Removed some unnecessary XJS that was mostly barfing
 out HTML wrapped in strings.

---
 web/root/index.xjs | 111 ++++++++++++++++-----------------------------
 1 file changed, 39 insertions(+), 72 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 0c61522d46..e1a0aac3aa 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -74,77 +74,6 @@
 		}
 	}
 
-	function writeAuthMenu() {
-		if (user.alias === settings.guest || user.number < 1) {
-			if (settings.user_registration) {
-				writeln(
-					'<li><a href="./?page=000-register.xjs">Register</a></li>'
-				);
-			}
-			writeln(
-				'<li class="dropdown">' +
-				'<a	href="#" ' +
-					'class="dropdown-toggle" ' +
-					'data-toggle="dropdown" ' +
-					'role="button" ' +
-					'aria-haspopup="true" ' +
-					'aria-expanded="false">' +
-				'Log in<span class="caret"></span></a>' +
-				'<div id="login-form" ' +
-					'class="dropdown-menu" ' +
-					'style="padding:15px; padding-bottom: 0px;"' +
-				'>' +
-				'<form id="form-login">' +
-				'<label for="input-username" class="sr-only">Username</label>' +
-				'<input id="input-username" ' +
-					'title="Username" ' +
-					'type="text" ' +
-					'class="dropdown form-control" ' +
-					'placeholder="Username"' +
-				'>' +
-				'<label for="input-password" class="sr-only">Password</label>' +
-				'<input id="input-password" ' +
-					'title="Password" ' +
-					'type="password" ' +
-					'class="dropdown form-control" ' +
-					'placeholder="Password"' +
-				'>' +
-				'<input id="button-login" ' +
-					'class="dropdown btn btn-primary" ' +
-					'type="submit" ' +
-					'value="Log in"' +
-					'>' +
-				'</form>' +
-				'</div>' +
-				'</li>'
-			);
-		} else {
-			writeln(
-				'<li class="dropdown">' +
-				'<a	href="#" ' +
-					'class="dropdown-toggle" ' +
-					'data-toggle="dropdown" ' +
-					'role="button" ' +
-					'aria-haspopup="true" ' +
-					'aria-expanded="false">' +
-					user.alias + 
-					' <span class="badge scanned" title="Unread mail" ' +
-					'id="badge-unread-mail"></span>' +
-					' <span class="caret"></span>' +
-				'</a>' +
-				'<ul class="dropdown-menu">' +
-				'<li><a href="./?page=000-mail.ssjs">Mail ' +
-				'<span class="badge scanned" ' +
-					'title="Unread mail" ' +
-					'id="badge-unread-mail-inner">' +
-				'</span>' +
-				'</a></li>' +
-				'<li><a id="button-logout" href="#">Log out</a></li>' +
-				'</ul>'
-			);
-		}
-	}
-
 	function writePage() {
 		var ini = getWebCtrl();
 		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
@@ -208,7 +137,45 @@
 				<div id="navbar" class="collapse navbar-collapse">
 					<ul class="nav navbar-nav"><?xjs writeNavBar(); ?></ul>
 					<ul class="nav navbar-nav navbar-right">
-						<?xjs writeAuthMenu(); ?>
+						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
+							<?xjs if (settings.user_registration) { ?>
+							<li><a href="./?page=000-register.xjs">Register</a></li>
+							<?xjs } ?>
+							<li class="dropdown">
+								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+									Log in
+									<span class="caret"></span>
+								</a>
+								<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
+									<form id="form-login">
+										<label for="input-username" class="sr-only">Username</label>
+										<input id="input-username" title="Username" type="text" class="dropdown form-control" placeholder="Username">
+										<label for="input-password" class="sr-only">Password</label>
+										<input id="input-password" title="Password" type="password" class="dropdown form-control" placeholder="Password">
+										<input id="button-login" class="dropdown btn btn-primary" type="submit" value="Log in">
+									</form>
+								</div>
+							</li>
+						<?xjs } else { ?>
+							<li class="dropdown">
+								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+									<?xjs write(user.alias); ?>
+									<span class="badge scanned" title="Unread mail" id="badge-unread-mail"></span>
+									<span class="caret"></span>
+								</a>
+								<ul class="dropdown-menu">
+									<li>
+										<a href="./?page=000-mail.ssjs">
+											Mail
+											<span class="badge scanned" title="Unread mail" id="badge-unread-mail-inner"></span>
+										</a>
+									</li>
+									<li>
+										<a id="button-logout" href="#">Log out</a>
+									</li>
+								</ul>
+							</li>
+						<?xjs } ?>
 					</ul>
 				</div>
 		  </div>
-- 
GitLab


From b9f33d1cefe5f9a21b87331e0b2bf8311458eb6b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 7 Jan 2017 16:09:50 -0500
Subject: [PATCH 228/752] Tidied up, slightly more readable.

---
 web/root/index.xjs | 59 +++++++++++++++++++---------------------------
 1 file changed, 24 insertions(+), 35 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index e1a0aac3aa..808995b6bb 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -6,38 +6,19 @@
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
 
-	if (typeof http_request.query.page === 'undefined'
-		||
-		!file_exists(
-			settings.web_root + 'pages/' +
-			file_getname(http_request.query.page[0])
-		)
-	) {
-		var page = '000-home.xjs';
-	} else {
-		var page = file_getname(http_request.query.page[0]);
-	}
-
-	function writeTitle() {
-		writeln(
-			getPageTitle(
-				settings.web_root + 'pages/' + page
-			).replace(/^HIDDEN(\:)*/, '')
-		);
-	}
+	var page = (
+		typeof http_request.query.page === 'undefined' ||
+		!file_exists(settings.web_root + 'pages/' + file_getname(http_request.query.page[0]))
+		? '000-home.xjs'
+		: file_getname(http_request.query.page[0])
+	);
 
 	function writeNavBar() {
 		var pages = getPages();
 		var primary = [];
 		var secondary = [];
 		pages.forEach(
-			function (thePage) {
-				if (thePage.primary) {
-					primary.push(thePage);
-				} else {
-					secondary.push(thePage);
-				}
-			}
+			function (e) { (e.primary ? primary : secondary).push(e); }
 		);
 		primary.forEach(
 			function (p) {
@@ -63,9 +44,7 @@
 				function (p) {
 					writeln(
 						'<li' +	(p.page === page ? ' class="active"' : '') + '>' +
-						'<a href="./?page=' + p.page + '">' +
-							p.title +
-						'</a>' +
+						'<a href="./?page=' + p.page + '">' + p.title + '</a>' +
 						'</li>'
 					);
 				}
@@ -91,12 +70,16 @@
 		<meta http-equiv="X-UA-Compatible" content="IE=edge">
 		<meta name="viewport" content="width=device-width, initial-scale=1">
 		<link rel="icon" href="./images/favicon.ico">
-		<title><?xjs writeTitle(); ?> :	<?xjs write(system.name); ?></title>
+		<title>
+			<?xjs write(getPageTitle(settings.web_root + 'pages/' + page).replace(/^HIDDEN(\:)*/, '')); ?>
+			:
+			<?xjs write(system.name); ?>
+		</title>
 		<link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
 		<link href="./css/offcanvas.css" rel="stylesheet">
 		<link href="./css/style.css" rel="stylesheet">
 		<?xjs if (file_exists(settings.web_root + 'css/custom.css')) { ?>
-		<link href="./css/custom.css" rel="stylesheet">
+			<link href="./css/custom.css" rel="stylesheet">
 		<?xjs } ?>
 	</head>
 
@@ -132,14 +115,20 @@
 						<span class="icon-bar"></span>
 						<span class="icon-bar"></span>
 					</button>
-					<a class="navbar-brand" href="./"><?xjs write(system.name); ?></a>
+					<a class="navbar-brand" href="./">
+						<?xjs write(system.name); ?>
+					</a>
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
-					<ul class="nav navbar-nav"><?xjs writeNavBar(); ?></ul>
+					<ul class="nav navbar-nav">
+						<?xjs writeNavBar(); ?>
+					</ul>
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
 							<?xjs if (settings.user_registration) { ?>
-							<li><a href="./?page=000-register.xjs">Register</a></li>
+								<li>
+									<a href="./?page=000-register.xjs">Register</a>
+								</li>
 							<?xjs } ?>
 							<li class="dropdown">
 								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
@@ -178,7 +167,7 @@
 						<?xjs } ?>
 					</ul>
 				</div>
-		  </div>
+			</div>
 		</nav>
 
 		<div class="container">
-- 
GitLab


From a37a5dbcb374a53f482bb872df61fa7b65daea9d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 8 Jan 2017 00:18:05 -0500
Subject: [PATCH 229/752] Provide a scope object when loading SSJS or XJS
 files.

---
 web/lib/sidebar.js | 43 +++++++++++++++++++------------------------
 1 file changed, 19 insertions(+), 24 deletions(-)

diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index 52c11e54b8..a3224c0d83 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -1,15 +1,20 @@
+function getFileContents(file) {
+	var ret = '';
+	var f = new File(file);
+	if (f.open('r')) {
+		ret = f.read();
+		f.close();
+	}
+	return ret;
+}
+
 function getSidebarModules() {
-	var sidebarModules = [];
-	var d = directory(settings.web_root + 'sidebar/*');
-	d.forEach(
-		function (item) {
-			if (file_isdir(item)) return;
-			var fn = file_getname(item);
-			// Check webctrl.ini
-			sidebarModules.push(fn);
-		}
+	return directory(settings.web_root + 'sidebar/*').reduce(
+		function (a, c) {
+			if (!file_isdir(c)) a.push(file_getname(c));
+			return a;
+		}, []
 	);
-	return sidebarModules;
 }
 
 function getSidebarModule(module) {
@@ -21,26 +26,16 @@ function getSidebarModule(module) {
 	switch (ext) {
 		case '.SSJS':
 			if (ext === '.SSJS' && module.search(/\.xjs\.ssjs$/i) >= 0) break;
-			load(module, true);
+			load({}, module, true);
 			break;
 		case '.XJS':
-			load(xjs_compile(module), true);
+			load({}, xjs_compile(module), true);
 			break;
 		case '.HTML':
-			var f = new File(module);
-			f.open('r');
-			if (f.is_open) {
-				ret = f.read();
-				f.close();
-			}
+			ret = getFileContents(module);
 			break;
 		case '.TXT':
-			var f = new File(module);
-			f.open();
-			if (f.is_open) {
-				ret = '<pre>' + f.read() + '</pre>';
-				f.close();
-			}
+			ret = '<pre>' + getFileContents(module) + '</pre>';
 			break;
 		default:
 			break;
-- 
GitLab


From 3fd0accf69b9133ea2ae46d9289b902719402459 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 8 Jan 2017 00:40:38 -0500
Subject: [PATCH 230/752] Make better use of XJS.

---
 web/root/index.xjs | 82 ++++++++++++++++++----------------------------
 1 file changed, 31 insertions(+), 51 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 808995b6bb..5867e6118f 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -13,46 +13,6 @@
 		: file_getname(http_request.query.page[0])
 	);
 
-	function writeNavBar() {
-		var pages = getPages();
-		var primary = [];
-		var secondary = [];
-		pages.forEach(
-			function (e) { (e.primary ? primary : secondary).push(e); }
-		);
-		primary.forEach(
-			function (p) {
-				writeln(
-					'<li' + (p.page === page ? ' class="active"' : '') + '>' +
-					'<a href="./?page=' + p.page + '">' + p.title +	'</a></li>'
-				);
-			}
-		);
-		if (secondary.length > 0) {
-			writeln(
-				'<li class="dropdown">' +
-				'<a	href="#" ' +
-					'class="dropdown-toggle" ' +
-					'data-toggle="dropdown" ' +
-					'role="button" aria-haspopup="true" ' +
-					'aria-expanded="false">' +
-					'More<span class="caret"></span>' +
-				'</a>' +
-				'<ul class="dropdown-menu">'
-			);
-			secondary.forEach(
-				function (p) {
-					writeln(
-						'<li' +	(p.page === page ? ' class="active"' : '') + '>' +
-						'<a href="./?page=' + p.page + '">' + p.title + '</a>' +
-						'</li>'
-					);
-				}
-			);
-			writeln('</ul></li>');
-		}
-	}
-
 	function writePage() {
 		var ini = getWebCtrl();
 		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
@@ -62,7 +22,6 @@
 
 ?>
 
-
 <!DOCTYPE html>
 <html lang="en">
 	<head>
@@ -121,7 +80,36 @@
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
 					<ul class="nav navbar-nav">
-						<?xjs writeNavBar(); ?>
+						<?xjs
+							var _primary = [];
+							var _secondary = [];
+							getPages().forEach(function (e) { (e.primary ? _primary : _secondary).push(e); });
+							 _primary.forEach(function (e) {
+						?>
+							<li<?xjs if (e.page === page) write(' class="active"'); ?>>
+								<a href="./?page=<?xjs write(e.page); ?>">
+									<?xjs write(e.title); ?>
+								</a>
+							</li>
+						<?xjs
+							});
+							if (_secondary.length > 0) {
+						?>
+							<li class="dropdown">
+								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+									More<span class="caret"></span>
+								</a>
+								<ul class="dropdown-menu">
+									<?xjs _secondary.forEach(function (e) { ?>
+										<li <?xjs if (e.page === page) write('class="active"'); ?>>
+											<a href="./?page=<?xjs write(e.page); ?>">
+												<?xjs write(e.title); ?>
+											</a>
+										</li>
+									<?xjs }); ?>
+								</ul>
+							</li>
+						<?xjs } ?>
 					</ul>
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
@@ -132,8 +120,7 @@
 							<?xjs } ?>
 							<li class="dropdown">
 								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-									Log in
-									<span class="caret"></span>
+									Log in<span class="caret"></span>
 								</a>
 								<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
 									<form id="form-login">
@@ -171,9 +158,7 @@
 		</nav>
 
 		<div class="container">
-			
 			<div class="row row-offcanvas row-offcanvas-right">
-
 				<div class="col-xs-12 col-sm-9">
 					<p class="pull-right visible-xs">
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
@@ -182,19 +167,14 @@
 					</p>
 					<?xjs writePage(); ?>
 				</div>
-
 				<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
 					<?xjs writeSidebarModules(); ?>
 				</div>
-
 			</div>
-
 		  	<hr>
-			
 			<footer>
 			<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
 			</footer>
-
 		</div>
 
 		<script src="./js/offcanvas.js"></script>
-- 
GitLab


From 8a7c0d4ecbb56d13deabba5eb1e93640839199e2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 8 Jan 2017 00:42:56 -0500
Subject: [PATCH 231/752] Provide a scope object when loading SSJS or XJS
 files.

---
 web/lib/pages.js | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 0a13db9978..0e778e8204 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -1,10 +1,8 @@
-load('sbbsdefs.js');
-
 function getWebCtrl() {
 	if (!file_exists(settings.web_root + 'pages/webctrl.ini')) return false;
 	var f = new File(settings.web_root + 'pages/webctrl.ini');
 	if (!f.open('r')) {
-		log(LOG_ERR, 'Unable to open pages/webctrl.ini');
+		log('Unable to open pages/webctrl.ini');
 		exit();
 	}
 	var ini = f.iniGetAllObjects();
@@ -16,7 +14,8 @@ function webCtrlTest(ini, filename) {
 	var ret = true;
 	for (var i = 0; i < ini.length; i++) {
 		if (!wildmatch(false, filename, ini[i].name)) continue;
-		if (typeof ini[i].AccessRequirements === 'undefined' ||
+		if (typeof ini[i].AccessRequirements === 'undefined'
+			||
 			user.compare_ars(ini[i].AccessRequirements)
 		) {
 			continue;
@@ -121,10 +120,10 @@ function getPage(page) {
 	switch(ext) {
 		case '.SSJS':
 			if (ext === '.SSJS' && page.search(/\.xjs\.ssjs$/i) >= 0) break;
-			load(page, true);
+			load({}, page, true);
 			break;
 		case '.XJS':
-			load(xjs_compile(page), true);
+			load({}, xjs_compile(page), true);
 			break;
 		case '.HTML':
 			var f = new File(page);
-- 
GitLab


From 50a9c86d49399579ac62f2da40082c5d06e1bd4e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 8 Jan 2017 12:03:42 -0500
Subject: [PATCH 232/752] Removed 000-mail.ssjs, added new 000-mail.xjs private
 mail page.  Added delete-mail API call and backend function for deletion of
 multiple messages.  Select-all and delete-selected-messages now available on
 the mail page.

---
 web/root/pages/000-mail.ssjs | 105 -----------------------------------
 web/root/pages/000-mail.xjs  | 102 ++++++++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+), 105 deletions(-)
 delete mode 100644 web/root/pages/000-mail.ssjs
 create mode 100644 web/root/pages/000-mail.xjs

diff --git a/web/root/pages/000-mail.ssjs b/web/root/pages/000-mail.ssjs
deleted file mode 100644
index a46a7a0219..0000000000
--- a/web/root/pages/000-mail.ssjs
+++ /dev/null
@@ -1,105 +0,0 @@
-//HIDDEN:Mail
-
-if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
-
-if (user.number === 0 || user.alias === settings.guest) exit();
-
-load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
-load(settings.web_lib + 'forum.js');
-
-writeln('<script type="text/javascript" src="./js/forum.js"></script>');
-
-if (typeof http_request.query.notice !== 'undefined') {
-	writeln(
-		'<div id="noticebox" class="alert alert-warning">' + 
-		http_request.query.notice[0] + '</div>' +
-		'<script type="text/javascript">' +
-		'$("#noticebox").fadeOut(3000,function(){$("#noticebox").remove();});' +
-		'</script>'
-	);
-}
-
-if (user.alias != settings.guest &&
-	!(user.security.restrictions&UFLAG_E) &&
-	!(user.security.restrictions&UFLAG_M)
-) {
-	writeln(
-		'<button class="btn btn-default icon" ' +
-		'aria-label="Post a new message" title="Post a new message" ' +
-		'onclick="addNew(\'mail\')">' +
-		'<span class="glyphicon glyphicon-pencil"></span>' +
-		'</button>'
-	);
-}
-
-
-writeln(
-	format(
-		'<ul class="nav nav-tabs">' +
-		'<li role="presentation" class="%s">' +
-		'<a href="./?page=%s&amp;sent=0">Inbox</a>' +
-		'</li>' +
-		'<li role="presentation" class="%s">' +
-		'<a href="./?page=%s&amp;sent=1">Sent</a>' +
-		'</li>' +
-		'</ul><br>',
-		(	typeof http_request.query.sent === 'undefined' ||
-			http_request.query.sent[0] == '0'
-			? 'active'
-			: ''
-		),
-		http_request.query.page[0],
-		(	typeof http_request.query.sent !== 'undefined' &&
-			http_request.query.sent[0] == '1'
-			? 'active'
-			: ''
-		),
-		http_request.query.page[0]
-	)
-);
-
-writeln('<ul id="forum-list-container" class="list-group">');
-
-function writeMessage(header) {
-	writeln(
-		format(
-			'<li id="li-%s" class="list-group-item mail striped %s">',
-			header.number,
-			(header.attr&MSG_READ ? 'read' : 'unread')
-		)
-	);
-	writeln(
-		format(
-			'<div style="cursor:pointer;" onclick="getMailBody(%s)">' +
-			'%s: <strong>%s</strong> on %s' +
-			'<p>Subject: <strong>%s</strong></p></div>' +
-			'<div class="message" id="message-%s" hidden></div>',
-			header.number,
-			(	typeof http_request.query.sent === 'undefined' ||
-				http_request.query.sent[0] == '0'
-				? 'From'
-				: 'To'
-			),
-			(	typeof http_request.query.sent === 'undefined' ||
-				http_request.query.sent[0] == '0'
-				? header.from
-				: header.to
-			),
-			(new Date(header.when_written_time * 1000)).toLocaleString(),
-			header.subject,
-			header.number
-		)
-	);
-	writeln('</li>');
-}
-
-getMailHeaders(
-	(	typeof http_request.query.sent === 'undefined' ||
-		http_request.query.sent[0] == '0'
-		? false
-		: true
-	)
-).forEach(writeMessage);
-
-writeln('</ul>');
\ No newline at end of file
diff --git a/web/root/pages/000-mail.xjs b/web/root/pages/000-mail.xjs
new file mode 100644
index 0000000000..0246f76a0b
--- /dev/null
+++ b/web/root/pages/000-mail.xjs
@@ -0,0 +1,102 @@
+<!--HIDDEN:Mail-->
+
+<?xjs
+
+if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+if (user.number == 0 || user.alias == settings.guest) exit();
+
+load('sbbsdefs.js');
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + 'forum.js');
+
+function sent() {
+	if (typeof http_request.query.sent === 'undefined') return false;
+	if (http_request.query.sent[0] == '0') return false;
+	return true;
+}
+
+?>
+
+<?xjs function writeMessage(header) { ?>
+	<li id="li-<?xjs write(header.number); ?>" class="list-group-item mail striped <?xjs write(header.attr&MSG_READ ? 'read' : 'unread'); ?>">
+		<div class="row">
+			<div class="col-sm-1">
+				<div class="checkbox">
+					<label class="checkbox-inline">
+						<input id="check-<?xjs write(header.number); ?>" type="checkbox" class="mail-select">
+					</label>
+				</div>
+			</div>
+			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
+				<?xjs write(!sent() ? 'From' : 'To'); ?>: 
+				<strong><?xjs write(!sent() ? header.from : header.to); ?></strong> 
+				on <?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
+				<p>Subject: <strong><?xjs write(header.subject); ?></strong></p>
+			</div>
+		</div>
+		<div class="message" id="message-<?xjs write(header.number); ?>" hidden></div>
+	</li>
+<?xjs } ?>
+
+<script type="text/javascript" src="./js/forum.js"></script>
+
+<script type="text/javascript">
+
+	function selectAllMail() {
+		$('input.mail-select').each(
+			function () {
+				$(this).prop('checked', $(this).prop('checked') ? false : true);
+			}
+		);
+	}
+
+	function deleteSelectedMail() {
+		var numbers = [];
+		$('input.mail-select:checked').each(
+			function () { numbers.push($(this).attr('id').split('-')[1]); }
+		);
+		$.getJSON(
+			'./api/forum.ssjs?call=delete-mail&number=' + numbers.join('&number='),
+			function (data) {
+				if (!data.success) return;
+				numbers.forEach(function (e) { $('#li-' + e).remove(); });
+			}
+		);
+	}
+
+</script>
+
+<?xjs if (typeof http_request.query.notice != "undefined") { ?>
+	<div id="noticebox" class="alert alert-warning">
+		<?xjs write(http_request.query.notice[0]); ?>
+	</div>
+	<script type="text/javascript">
+		$("#noticebox").fadeOut(3000, function () { $("#noticebox").remove(); });
+	</script>
+<?xjs } ?>
+
+<?xjs if (!(user.security.restrictions&UFLAG_E) && !(user.security.restrictions&UFLAG_M)) { ?>
+	<button class="btn btn-default icon" aria-label="Post a new message" title="Post a new message" onclick="addNew('mail')">
+		<span class="glyphicon glyphicon-pencil"></span>
+	</button>
+	<button class="btn btn-default icon" aria-label="Select all messages" title="Select all messages" onclick="selectAllMail()">
+		<span class="glyphicon glyphicon-check"></span>
+	</button>
+	<button class="btn btn-default icon" aria-label="Delete selected messages" title="Delete selected messages" onclick="deleteSelectedMail()">
+		<span class="glyphicon glyphicon-trash"></span>
+	</button>
+<?xjs } ?>
+
+<ul class="nav nav-tabs">
+	<li role="presentation" class="<?xjs write(!sent() ? ' active' : ''); ?>">
+		<a href="./?page=<?xjs write(page); ?>&amp;sent=0">Inbox</a>
+	</li>
+	<li role="presentation" class="<?xjs write(sent() ? ' active' : ''); ?>">
+		<a href="./?page=<?xjs write(page); ?>&amp;sent=1">Sent</a>
+	</li>
+</ul>
+<br>
+
+<ul id="forum-list-container" class="list-group">
+	<?xjs getMailHeaders(sent()).forEach(writeMessage); ?>
+</ul>
-- 
GitLab


From 7ec109cdd8a53d9345353dda0603272e9395fca6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 8 Jan 2017 12:04:23 -0500
Subject: [PATCH 233/752] Removed 000-mail.ssjs, added new 000-mail.xjs private
 mail page.  Added delete-mail API call and backend function for deletion of
 multiple messages.  Select-all and delete-selected-messages now available on
 the mail page.

---
 web/lib/forum.js        | 20 ++++++++-
 web/root/api/forum.ssjs | 96 +++++++++++++++++++++++++----------------
 2 files changed, 79 insertions(+), 37 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index da1b292f42..231f32cbc4 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -572,7 +572,6 @@ function deleteMessage(sub, number) {
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return false;
     var header = msgBase.get_msg_header(number);
-	log(JSON.stringify(header));
     if (header === null) return false;
     if (sub === 'mail' &&
         (header.to_ext == user.number || header.from_ext == user.number)
@@ -587,6 +586,25 @@ function deleteMessage(sub, number) {
     return ret;
 }
 
+function deleteMail(numbers) {
+    if (typeof numbers === 'undefined' || !Array.isArray(numbers)) return false;
+    var msgBase = new MsgBase('mail');
+    if (!msgBase.open()) return false;
+    numbers.forEach(
+        function (e) {
+            e = parseInt(e);
+            if (isNaN(e) || e < msgBase.first_msg || e > msgBase.last_msg) return;
+            var header = msgBase.get_msg_header(e);
+            if (header === null) return;
+            if (header.to_ext == user.number || header.from_ext == user.number) {
+                msgBase.remove_msg(e);
+            }
+        }
+    );
+    msgBase.close();
+    return true;
+}
+
 function voteMessage(sub, number, up) {
     if (typeof msg_area.sub[sub] === 'undefined' && sub !== 'mail') {
         return false;
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 063eca097f..fe15e56154 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -23,42 +23,6 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 
         switch(http_request.query.call[0].toLowerCase()) {
 
-            case 'list-groups':
-                reply = listGroups();
-                break;
-            
-            case 'list-subs':
-                if (typeof http_request.query.group !== 'undefined') {
-                    reply = listSubs(http_request.query.group[0]);
-                }
-                break;
-            
-            case 'list-threads':
-                if (typeof http_request.query.sub !== 'undefined') {
-                    reply = listThreads(http_request.query.sub[0]);
-                }
-                break;
-            
-            case 'get-group-unread-count':
-                if (typeof http_request.query.group !== 'undefined') {
-                    http_request.query.group.forEach(
-                        function(group) {
-                            reply[group] = getGroupUnreadCount(group);
-                        }
-                    );
-                }
-                break;
-
-            case 'get-sub-unread-count':
-                if (typeof http_request.query.sub !== 'undefined') {
-                    http_request.query.sub.forEach(
-                        function(sub) {
-                            reply[sub] = getSubUnreadCount(sub);
-                        }
-                    );
-                }
-                break;
-
             case 'get-mail-unread-count':
                 reply.count = getMailUnreadCount();
                 break;
@@ -118,6 +82,14 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 }
                 break;
 
+            case 'delete-mail':
+                if (typeof http_request.query.number !== 'undefined') {
+                    reply.success = deleteMail(http_request.query.number);
+                } else {
+                    reply.success = false;
+                }
+                break;
+
             case 'set-scan-cfg':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.cfg !== 'undefined'
@@ -188,7 +160,9 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 
     // Unauthenticated calls
     if (!handled) {
+    
         switch(http_request.query.call[0].toLowerCase()) {
+
             case 'get-thread-votes':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.id !== 'undefined'
@@ -202,11 +176,13 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     }
                 }
                 break;
+            
             case 'get-sub-votes':
                 if (typeof http_request.query.sub !== 'undefined') {
                     reply = getVotesInThreads(http_request.query.sub[0]);
                 }
                 break;
+            
             case 'get-poll-results':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.id !== 'undefined'
@@ -217,9 +193,57 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     );
                 }
                 break;
+
+            case 'list-groups':
+                reply = listGroups();
+                break;
+            
+            case 'list-subs':
+                if (typeof http_request.query.group !== 'undefined') {
+                    reply = listSubs(http_request.query.group[0]);
+                }
+                break;
+            
+            case 'list-threads':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.offset !== 'undefined'
+                ) {
+                    if (typeof http_request.query.count !== 'undefined') {
+                        var count = http_request.query.count[0];
+                    }
+                    reply = listThreads(
+                        http_request.query.sub[0],
+                        http_request.query.offset[0],
+                        count || settings.page_size
+                    );
+                }
+                break;
+            
+            case 'get-group-unread-count':
+                if (typeof http_request.query.group !== 'undefined') {
+                    http_request.query.group.forEach(
+                        function(group) {
+                            reply[group] = getGroupUnreadCount(group);
+                        }
+                    );
+                }
+                break;
+
+            case 'get-sub-unread-count':
+                if (typeof http_request.query.sub !== 'undefined') {
+                    http_request.query.sub.forEach(
+                        function(sub) {
+                            reply[sub] = getSubUnreadCount(sub);
+                        }
+                    );
+                }
+                break;
+            
             default:
                 break;
+        
         }
+    
     }
 
 }
-- 
GitLab


From 369b2c289d097e6a0711392cf624f44a8ea49ec9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 8 Jan 2017 12:54:15 -0500
Subject: [PATCH 234/752] Mail link points to 000-mail.xjs now.

---
 web/root/index.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 5867e6118f..92f01e307d 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -141,7 +141,7 @@
 								</a>
 								<ul class="dropdown-menu">
 									<li>
-										<a href="./?page=000-mail.ssjs">
+										<a href="./?page=000-mail.xjs">
 											Mail
 											<span class="badge scanned" title="Unread mail" id="badge-unread-mail-inner"></span>
 										</a>
-- 
GitLab


From 4e371f687956b3a7b861d6b7f0923b360f139336 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 15:48:07 -0500
Subject: [PATCH 235/752] New navbar submenu system.

Create a dropdown menu by adding a subdirectory to web/root/pages.
The menu name will be the same as the directory name.
Directories beginning with . will be ignored.
Ordering of submenus can be enforced by prefixing directory names with ###-, same as with pages; the prefix will be stripped prior to display.

Nested subdirectories will create nested submenus.  This will probably get ugly at a certain depth, so please enjoy responsibly.
Each subdirectory can (and should) contain a webctrl.ini file, which will be enforced.

The default 'More' menu no longer works with the '###_' filename prefix.  It has been replaced by a 'More' subdirectory.
If you were using that system, move those files into the 'More' subdirectory.
---
 web/lib/pages.js                     |  78 ++++++-----
 web/root/css/style.css               |  10 ++
 web/root/index.xjs                   | 103 +++++++++-----
 web/root/pages/More/001-userlist.xjs | 201 +++++++++++++++++++++++++++
 web/root/pages/More/webctrl.ini      |   6 +
 web/root/pages/webctrl.ini           |   4 +-
 6 files changed, 327 insertions(+), 75 deletions(-)
 create mode 100644 web/root/pages/More/001-userlist.xjs
 create mode 100644 web/root/pages/More/webctrl.ini

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 0e778e8204..6e2f236648 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -1,8 +1,8 @@
-function getWebCtrl() {
-	if (!file_exists(settings.web_root + 'pages/webctrl.ini')) return false;
-	var f = new File(settings.web_root + 'pages/webctrl.ini');
+function getWebCtrl(dir) {
+	if (!file_exists(dir + 'webctrl.ini')) return false;
+	var f = new File(dir + 'webctrl.ini');
 	if (!f.open('r')) {
-		log('Unable to open pages/webctrl.ini');
+		log('Unable to open ' + dir + 'webctrl.ini');
 		exit();
 	}
 	var ini = f.iniGetAllObjects();
@@ -14,8 +14,7 @@ function webCtrlTest(ini, filename) {
 	var ret = true;
 	for (var i = 0; i < ini.length; i++) {
 		if (!wildmatch(false, filename, ini[i].name)) continue;
-		if (typeof ini[i].AccessRequirements === 'undefined'
-			||
+		if (typeof ini[i].AccessRequirements === 'undefined' ||
 			user.compare_ars(ini[i].AccessRequirements)
 		) {
 			continue;
@@ -26,46 +25,51 @@ function webCtrlTest(ini, filename) {
 	return ret;
 }
 
-function webCtrlFilter(pages) {
-	var ini = getWebCtrl();
-	if (typeof ini === 'boolean' && !ini) return pages;
-	pages = pages.filter(
-		function (page) {
-			return webCtrlTest(ini, page.page);
-		}
-	);
-	return pages;
-}
+function getPageList(dir) {
 
-function getPages() {
-
-	var pages = [];
-	var d = directory(settings.web_root + 'pages/*');
-	d.forEach(
-		function (item) {
-			if (file_isdir(item)) return;
-			var fn = file_getname(item);
-			var title = getPageTitle(item);
-			if (typeof title === 'undefined' ||
-				title.search(/^HIDDEN/) === 0 ||
-				fn.search(/\.xjs\.ssjs$/i) >= 0
-			) {
-				return;
-			}
-			pages.push(
-				{	'page' : fn,
-					'title' : title,
-					'primary' : (fn.search(/^\d+_/) === 0 ? false : true)
+	dir = backslash(fullpath(dir));
+	if (dir.indexOf(backslash(fullpath(settings.web_root + '/pages'))) !== 0) {
+		return {};
+	}
+
+	var webctrl = getWebCtrl(dir);
+
+	var pages = {};
+	directory(dir + '*').forEach(
+		function (e) {
+			if (file_isdir(e)) {
+				var list = getPageList(e);
+				if (Object.keys(list).length > 0) {
+					pages[e.split(e.substr(-1, 1)).slice(-2, -1)] = list;
 				}
-			);
+			} else if (e.search(/(\.xjs\.ssjs|webctrl\.ini)$/i) < 0) {
+				var fn = file_getname(e);
+				var title = getPageTitle(e);
+				if ((	typeof webctrl === 'undefined' ||
+						webCtrlTest(webctrl, fn)
+					) && title.search(/^HIDDEN/) < 0
+				) {
+					pages[title] = file_getname(e);
+				}
+			}
 		}
 	);
-	return webCtrlFilter(pages);
+
+	return pages;
 
 }
 
 function getPageTitle(file) {
 
+	if (backslash(
+			fullpath(file)
+		).indexOf(
+			backslash(fullpath(settings.web_root + '/pages'))
+		) !== 0
+	) {
+		return;
+	}
+
 	var title;
 	var ext = file_getext(file).toUpperCase();
 	var f = new File(file);
diff --git a/web/root/css/style.css b/web/root/css/style.css
index 0cbb2a8d14..b3563f5a0a 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -151,4 +151,14 @@ animation: indicator-fade 3s ease 0s 1 alternate !important;
 @keyframes indicator-fade {
     from { background-color: #82E0AA;}
     to { background-color: transparent; }
+}
+
+.dropdown-submenu {
+    position: relative;
+}
+
+.dropdown-submenu .dropdown-menu {
+    top: 0;
+    left: 100%;
+    margin-top: -1px;
 }
\ No newline at end of file
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 92f01e307d..95cf8c535f 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -7,14 +7,24 @@
 	load(settings.web_lib + 'sidebar.js');
 
 	var page = (
-		typeof http_request.query.page === 'undefined' ||
-		!file_exists(settings.web_root + 'pages/' + file_getname(http_request.query.page[0]))
+		(	typeof http_request.query.page === 'undefined' ||
+			!file_exists(
+				fullpath(
+					settings.web_root + 'pages/' + http_request.query.page[0]
+				)
+			) ||
+			fullpath(
+				settings.web_root + 'pages/' + http_request.query.page[0]
+			).indexOf(settings.web_root + 'pages') !== 0
+		)
 		? '000-home.xjs'
-		: file_getname(http_request.query.page[0])
+		: http_request.query.page[0]
 	);
 
 	function writePage() {
-		var ini = getWebCtrl();
+		var ini = getWebCtrl(
+			settings.web_root + 'pages/' + page.replace(file_getname(page), '')
+		);
 		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
 			write(getPage(page));
 		}
@@ -30,6 +40,7 @@
 		<meta name="viewport" content="width=device-width, initial-scale=1">
 		<link rel="icon" href="./images/favicon.ico">
 		<title>
+			<?xjs log(LOG_ERR, 'PAGE: ' + page); ?>
 			<?xjs write(getPageTitle(settings.web_root + 'pages/' + page).replace(/^HIDDEN(\:)*/, '')); ?>
 			:
 			<?xjs write(system.name); ?>
@@ -79,38 +90,43 @@
 					</a>
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
+
+					<?xjs function subMenu(obj, title, path) { ?>
+						<li class="dropdown<?xjs if (path.match(/\//g).length > 1) write('-submenu'); ?>">
+							<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+								<?xjs write(title.replace(/^.*\-/, '')); ?>
+								<span class="caret"></span>
+							</a>
+							<ul class="dropdown-menu">
+								<?xjs menu(obj, path); ?>
+							</ul>
+						</li>
+					<?xjs } ?>
+
+					<?xjs
+						function menu(obj, path) {
+							Object.keys(obj).forEach(
+								function (e) {
+									if (typeof obj[e] === 'object') {
+										subMenu(obj[e], e, (path || '') + e + '/');
+									} else {
+					?>
+									<li>
+										<a href="./?page=<?xjs write((path || '') + obj[e]); ?>">
+											<?xjs write(e); ?>
+										</a>
+									</li>
+					<?xjs
+									}
+								}
+							);
+						}
+					?>
+
 					<ul class="nav navbar-nav">
-						<?xjs
-							var _primary = [];
-							var _secondary = [];
-							getPages().forEach(function (e) { (e.primary ? _primary : _secondary).push(e); });
-							 _primary.forEach(function (e) {
-						?>
-							<li<?xjs if (e.page === page) write(' class="active"'); ?>>
-								<a href="./?page=<?xjs write(e.page); ?>">
-									<?xjs write(e.title); ?>
-								</a>
-							</li>
-						<?xjs
-							});
-							if (_secondary.length > 0) {
-						?>
-							<li class="dropdown">
-								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-									More<span class="caret"></span>
-								</a>
-								<ul class="dropdown-menu">
-									<?xjs _secondary.forEach(function (e) { ?>
-										<li <?xjs if (e.page === page) write('class="active"'); ?>>
-											<a href="./?page=<?xjs write(e.page); ?>">
-												<?xjs write(e.title); ?>
-											</a>
-										</li>
-									<?xjs }); ?>
-								</ul>
-							</li>
-						<?xjs } ?>
+						<?xjs menu(getPageList(settings.web_root + 'pages/')); ?>
 					</ul>
+
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
 							<?xjs if (settings.user_registration) { ?>
@@ -179,6 +195,23 @@
 
 		<script src="./js/offcanvas.js"></script>
 
+	<script>
+		$(document).ready(
+			function () {
+				$('a.dropdown-toggle').on(
+					"click", function (e) {
+						$(this).next('ul').toggle();
+						$(this).next('div').toggle();
+						e.stopPropagation();
+						e.preventDefault();
+					}
+				);
+			}
+		);
+	</script>
+
+	<?xjs if (user.alias === 'echicken') write(JSON.stringify(getPageList(settings.web_root + 'pages/'))); ?>
+
 	</body>
 
-</html>
+</html>
\ No newline at end of file
diff --git a/web/root/pages/More/001-userlist.xjs b/web/root/pages/More/001-userlist.xjs
new file mode 100644
index 0000000000..63358a3bfc
--- /dev/null
+++ b/web/root/pages/More/001-userlist.xjs
@@ -0,0 +1,201 @@
+<!--User List-->
+<?xjs
+	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+
+	var pageSize = 500;
+
+	// Uncomment/comment properties below to enable/disable list columns
+	// Rearrange to change order of appearance
+	var columns = {
+		// number : { name : "#", type : "number" },
+		alias : { name : 'Alias', type : 'string' },
+		// name : { name : "Name", type : "string" },
+		// age : { name : "Age", type : "number" },
+		// gender : { name : "Sex", type : "string" },
+		location : { name : 'Location', type : 'string' },
+		laston_date : { name : 'Last On', type : 'date' },
+		// connection : { name : "Via", type : "string" },
+		// firston_date : { name : "First On", type : "date" },
+		// total_logons : { name : "Calls", type : "number" },
+		// total_posts : { name : "Posts", type : "number" }
+	};
+
+	// Most people won't need to edit below this line
+	load('sbbsdefs.js');
+
+	if (typeof http_request.query.offset === 'undefined' ||
+		isNaN(parseInt(http_request.query.offset[0])) ||
+		parseInt(http_request.query.offset[0]) < 1
+	) {
+		var offset = 0;
+	} else {
+		var offset = parseInt(http_request.query.offset[0]);
+	}
+
+	var url = format(
+		'http://%s%s',
+		http_request.host,
+		http_request.request_string.split("&")[0]
+	);
+	
+	function sortUser(a, b, sortOrder, type) {
+		if (type === 'string') {
+			a = a.toUpperCase();
+			b = b.toUpperCase();
+			ret = (
+				a < b
+				? (sortOrder === 'ascending' ? 1 : -1)
+				: (	a > b
+					? (sortOrder === 'ascending' ? -1 : 1)
+					: 0
+				)
+			);
+		} else if (type === 'number' || type === 'date') {
+			ret = (
+				a < b
+				? (sortOrder === 'ascending' ? -1 : 1)
+				: ( a > b
+					? (sortOrder === 'ascending' ? 1 : -1)
+					: 0
+				)
+			);
+		}
+		return ret;
+	}
+
+	function sortUsers(a, b) {
+		var ret = 0;
+		if (typeof http_request.query.sortby !== 'undefined' &&
+			typeof http_request.query.sortorder !== 'undefined'	&&
+			(	http_request.query.sortorder[0] === 'ascending' ||
+				http_request.query.sortorder[0] === 'descending'
+			)
+		) {
+			var sortBy = http_request.query.sortby[0].toLowerCase();
+			var sortOrder = http_request.query.sortorder[0].toLowerCase();
+			for (var c in columns) {
+				if (sortBy != c) continue;
+				ret = sortUser(a[c], b[c], sortOrder, columns[c].type);
+				break;
+			}
+		}
+		return ret;
+	}
+	
+	function makeSortURLs(field, order) {
+		return format(
+			'<a class="icon" ' +
+			'href="./?page=%s&sortby=%s&sortorder=ascending&offset=%s">' +
+			'<span class="glyphicon glyphicon-arrow-up"></span>' +
+			'</a>' +
+			'<a class="icon" ' +
+			'href="./?page=%s&sortby=%s&sortorder=descending&offset=%s">' +
+			'<span class="glyphicon glyphicon-arrow-down"></span>' +
+			'</a>',
+			http_request.query.page[0],
+			field,
+			offset,
+			http_request.query.page[0],
+			field,
+			offset
+		);
+	}
+
+	function makePagerURLs() {
+		var ret = { previous : '', next : '' };
+		if (offset > 1) {
+			ret.previous = format(
+				'<li><a href="./?page=%s&offset=%s">Previous</a></li>',
+				http_request.query.page[0],
+				Math.max(0, offset - pageSize)
+			);
+		}
+		if (system.lastuser - offset > pageSize) {
+			ret.next = format(
+				'<li><a href="./?page=%s&offset=%s">Next</a></li>',
+				http_request.query.page[0],
+				Math.min(
+					offset + pageSize, offset + (system.lastuser - offset)
+				)
+			);
+		}
+		return ret;
+	}
+	
+	function copyProperties(source, dest) {
+		for (var property in source) {
+			if ((	typeof source[property] === 'string' ||
+					typeof source[property] === 'number'
+				) && typeof columns[property] !== 'undefined'
+			) {
+				dest[property] = source[property];
+			} else if (property === 'stats') {
+				for (var stat in source.stats) {
+					if (typeof columns[stat] === 'undefined') continue;
+					dest[stat] = source.stats[stat];
+				}
+			}
+		}
+		return dest;
+	}
+
+	function loadUsers(offset, pageSize) {
+		var users = [];
+		for (var u = 1; u < system.lastuser; u++) {
+			var usr = new User(u);
+			if (usr.settings&USER_DELETED ||
+				usr.compare_ars('REST Q') ||
+				usr.alias === 'Guest' ||
+				usr.alias === settings.guest
+			) {
+				continue;
+			}
+			users.push(copyProperties(usr, {}));
+		}
+		users.sort(sortUsers);
+		return users.slice(offset, offset + pageSize);
+	}
+
+	function writeColumns() {
+		for (var c in columns) {
+			writeln('<th>' + columns[c].name + ' ' + makeSortURLs(c) + '</th>');
+		}
+	}
+
+	function writeRows(users) {
+		for (var u = 0; u < users.length; u++) {
+			writeln('<tr>');
+			for (var c in columns) {
+				writeln(
+					'<td>' +
+					(	columns[c].type === 'date'
+						? system.timestr(users[u][c])
+						: users[u][c].toString()
+					) +	'</td>'
+				);
+			}
+			writeln('</tr>');
+		}
+	}
+
+	var pager = makePagerURLs();
+
+?>
+
+<div class="well well-sm"><h3>User List</h3></div>
+<table class="table table-striped">
+	<thead>
+		<tr>
+			<?xjs writeColumns(); ?>
+		</tr>
+	</thead>
+	<tbody>
+		<?xjs writeRows(loadUsers(offset, pageSize)); ?>
+	</tbody>
+</table>
+
+<nav>
+	<ul class="pager">
+		<?xjs writeln(pager.previous); writeln(pager.next); ?>
+	</ul>
+</nav>
\ No newline at end of file
diff --git a/web/root/pages/More/webctrl.ini b/web/root/pages/More/webctrl.ini
new file mode 100644
index 0000000000..b5b2b7570a
--- /dev/null
+++ b/web/root/pages/More/webctrl.ini
@@ -0,0 +1,6 @@
+AccessRequirements = level 90
+Authorization = Digest
+
+[*userlist.xjs]
+AccessRequirements = LEVEL 50 AND REST NOT G
+
diff --git a/web/root/pages/webctrl.ini b/web/root/pages/webctrl.ini
index f39b1514aa..8593d5892e 100644
--- a/web/root/pages/webctrl.ini
+++ b/web/root/pages/webctrl.ini
@@ -1,8 +1,6 @@
 AccessRequirements = level 90
 Authorization = Digest
 
-[*userlist.xjs]
+[*games.xjs]
 AccessRequirements = LEVEL 50 AND REST NOT G
 
-[*games.xjs]
-AccessRequirements = LEVEL 50 AND REST NOT G
\ No newline at end of file
-- 
GitLab


From cd635828dbf43bf7214948da36511676e2f36e14 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 15:57:08 -0500
Subject: [PATCH 236/752] Removed file.

---
 web/root/pages/003_userlist.xjs | 215 --------------------------------
 1 file changed, 215 deletions(-)
 delete mode 100644 web/root/pages/003_userlist.xjs

diff --git a/web/root/pages/003_userlist.xjs b/web/root/pages/003_userlist.xjs
deleted file mode 100644
index 246e0e37e1..0000000000
--- a/web/root/pages/003_userlist.xjs
+++ /dev/null
@@ -1,215 +0,0 @@
-<!--User List-->
-<?xjs
-	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
-
-	var pageSize = 500;
-
-	// Uncomment/comment properties below to enable/disable list columns
-	// Rearrange to change order of appearance
-	var columns = {
-		// number : { name : "#", type : "number" },
-		alias : { name : 'Alias', type : 'string' },
-		// name : { name : "Name", type : "string" },
-		// age : { name : "Age", type : "number" },
-		// gender : { name : "Sex", type : "string" },
-		location : { name : 'Location', type : 'string' },
-		laston_date : { name : 'Last On', type : 'date' },
-		// connection : { name : "Via", type : "string" },
-		// firston_date : { name : "First On", type : "date" },
-		// total_logons : { name : "Calls", type : "number" },
-		// total_posts : { name : "Posts", type : "number" }
-	};
-
-	// Most people won't need to edit below this line
-	load('sbbsdefs.js');
-	load('js-date-format.js');
-
-	if (typeof http_request.query.offset === 'undefined' ||
-		isNaN(parseInt(http_request.query.offset[0])) ||
-		parseInt(http_request.query.offset[0]) < 1
-	) {
-		var offset = 0;
-	} else {
-		var offset = parseInt(http_request.query.offset[0]);
-	}
-
-	var url = format(
-		'http://%s%s',
-		http_request.host,
-		http_request.request_string.split("&")[0]
-	);
-	
-	function sortUser(a, b, sortOrder, type) {
-		if (type === 'string') {
-			a = a.toUpperCase();
-			b = b.toUpperCase();
-			ret = (
-				a < b
-				? (sortOrder === 'ascending' ? 1 : -1)
-				: (	a > b
-					? (sortOrder === 'ascending' ? -1 : 1)
-					: 0
-				)
-			);
-		} else if (type === 'number' || type === 'date') {
-			ret = (
-				a < b
-				? (sortOrder === 'ascending' ? -1 : 1)
-				: ( a > b
-					? (sortOrder === 'ascending' ? 1 : -1)
-					: 0
-				)
-			);
-		}
-		return ret;
-	}
-
-	function sortUsers(a, b) {
-		var ret = 0;
-		if (typeof http_request.query.sortby !== 'undefined' &&
-			typeof http_request.query.sortorder !== 'undefined'	&&
-			(	http_request.query.sortorder[0] === 'ascending' ||
-				http_request.query.sortorder[0] === 'descending'
-			)
-		) {
-			var sortBy = http_request.query.sortby[0].toLowerCase();
-			var sortOrder = http_request.query.sortorder[0].toLowerCase();
-			for (var c in columns) {
-				if (sortBy != c) continue;
-				ret = sortUser(a[c], b[c], sortOrder, columns[c].type);
-				break;
-			}
-		}
-		return ret;
-	}
-	
-	function makeSortURLs(field, order) {
-		return format(
-			'<a class="icon" ' +
-			'href="./?page=%s&sortby=%s&sortorder=ascending&offset=%s">' +
-			'<span class="glyphicon glyphicon-arrow-up"></span>' +
-			'</a>' +
-			'<a class="icon" ' +
-			'href="./?page=%s&sortby=%s&sortorder=descending&offset=%s">' +
-			'<span class="glyphicon glyphicon-arrow-down"></span>' +
-			'</a>',
-			http_request.query.page[0],
-			field,
-			offset,
-			http_request.query.page[0],
-			field,
-			offset
-		);
-	}
-
-	function makePagerURLs() {
-		var ret = { previous : '', next : '' };
-		if (offset > 1) {
-			ret.previous = format(
-				'<li><a href="./?page=%s&offset=%s">Previous</a></li>',
-				http_request.query.page[0],
-				Math.max(0, offset - pageSize)
-			);
-		}
-		if (system.lastuser - offset > pageSize) {
-			ret.next = format(
-				'<li><a href="./?page=%s&offset=%s">Next</a></li>',
-				http_request.query.page[0],
-				Math.min(
-					offset + pageSize, offset + (system.lastuser - offset)
-				)
-			);
-		}
-		return ret;
-	}
-	
-	function copyProperties(source, dest) {
-		for (var property in source) {
-			if ((	typeof source[property] === 'string' ||
-					typeof source[property] === 'number'
-				) && typeof columns[property] !== 'undefined'
-			) {
-				dest[property] = source[property];
-			} else if (property === 'stats') {
-				for (var stat in source.stats) {
-					if (typeof columns[stat] === 'undefined') continue;
-					dest[stat] = source.stats[stat];
-				}
-			}
-		}
-		return dest;
-	}
-
-	function loadUsers(offset, pageSize) {
-		var users = [];
-		for (var u = 1; u <= system.lastuser; u++) {
-			var usr = new User(u);
-			if (usr.settings&USER_DELETED ||
-				usr.compare_ars('REST Q') ||
-				usr.alias === 'Guest' ||
-				usr.alias === settings.guest
-			) {
-				continue;
-			}
-			users.push(copyProperties(usr, {}));
-		}
-		users.sort(sortUsers);
-		return users.slice(offset, offset + pageSize);
-	}
-
-	function writeColumns() {
-		for (var c in columns) {
-			writeln('<th>' + columns[c].name + ' ' + makeSortURLs(c) + '</th>');
-		}
-	}
-
-	function writeRows(users) {
-		for (var u = 0; u < users.length; u++) {
-			writeln('<tr>');
-			for (var c in columns) {
-				var cellString = '';
-				if ( columns[c].type === 'date' ) {
-					// Generate JS dates
-					var theDate = new Date( users[u][c] * 1000);
-					var todayDate = new Date();
-					// Find how many days between last login date and today.
-					var diff = (Math.abs(theDate.getTime() - todayDate.getTime()) ) / (1000 * 60 * 60 * 24);
-					// If the date is within the last week, print just the day of the week
-					if (diff < 8) {
-						cellString = theDate.getDayName();
-					}
-					// Otherwise, print a date
-					else {
-						cellString = theDate.format('MMM D, YYYY');
-					}
-				}
-				else {
-					cellString = users[u][c].toString();
-				}
-				writeln( '<td>' + cellString + '</td>' );
-			}
-			writeln('</tr>');
-		}
-	}
-
-	var pager = makePagerURLs();
-
-?>
-
-<div class="well well-sm"><h3>User List</h3></div>
-<table class="table table-striped">
-	<thead>
-		<tr>
-			<?xjs writeColumns(); ?>
-		</tr>
-	</thead>
-	<tbody>
-		<?xjs writeRows(loadUsers(offset, pageSize)); ?>
-	</tbody>
-</table>
-
-<nav>
-	<ul class="pager">
-		<?xjs writeln(pager.previous); writeln(pager.next); ?>
-	</ul>
-</nav>
\ No newline at end of file
-- 
GitLab


From 78ca75a11d6a53c5975533c1eb3e2899d30f26a8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 15:58:56 -0500
Subject: [PATCH 237/752] Removed note about underscore in page filename
 prefix.

---
 README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/README.md b/README.md
index 0fff57e395..481184b41b 100644
--- a/README.md
+++ b/README.md
@@ -124,7 +124,6 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 	- You can place your own CSS overrides in *web/root/css/custom.css*
 	- You can load another stylesheet of your own choosing in the &lt;head&gt; section of *web/root/index.xjs* (load it after the others)
 - The sidebar module & page structure is *mostly* similar to the system used in ecWeb v3.  See [the old instructions](http://wiki.synchro.net/howto:ecweb#the_sidebar) for info on adding content.
-	- You can force a link to a page to be placed in the *More* menu by using an underscore as a separator in its filename rather than a hyphen
 
 ###Uninstall
 
-- 
GitLab


From e757beb05500df8190940eeec550fe5185a2c782 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 16:09:35 -0500
Subject: [PATCH 238/752] Replaced SSJS files page with an XJS version.

---
 web/root/pages/002-files.ssjs | 125 ----------------------------------
 web/root/pages/002-files.xjs  | 100 +++++++++++++++++++++++++++
 2 files changed, 100 insertions(+), 125 deletions(-)
 delete mode 100644 web/root/pages/002-files.ssjs
 create mode 100644 web/root/pages/002-files.xjs

diff --git a/web/root/pages/002-files.ssjs b/web/root/pages/002-files.ssjs
deleted file mode 100644
index ab6069b681..0000000000
--- a/web/root/pages/002-files.ssjs
+++ /dev/null
@@ -1,125 +0,0 @@
-//Files
-
-if(typeof argv[0] !== 'boolean' || !argv[0])
-	exit();
-
-load(system.exec_dir + '../web/lib/init.js');
-load(settings.web_lib + 'files.js');
-
-//writeln('<script type="text/javascript" src="./js/files.js"></script>');
-
-if (typeof http_request.query.dir !== 'undefined' &&
-	typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
-) {
-	// File listing
-
-	writeln(
-		'<ol class="breadcrumb">' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
-		'</li>' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + file_area.dir[http_request.query.dir[0]].lib_index + '">' + file_area.dir[http_request.query.dir[0]].lib_name + '</a>' +
-		'</li>' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;dir=' + http_request.query.dir[0] + '">' + http_request.query.dir[0] + '</a>' +
-		'</li>' +
-		'</ol>'
-	);
-
-	function writeFileDetails(file) {
-		var description = '<p>' + file.desc + '</p>';
-		if (typeof file.extdesc !== 'undefined') {
-			if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) {
-				description = '<pre class="ansi">' + html_encode(file.extdesc, true, false, true, true) + '</pre>';
-			} else {
-				description = '<pre class="list">' + file.extdesc.replace(/[^\r,\n,\x20-\x7E]/g, "") + '</pre>';
-			}
-		}
-		writeln(
-			format(
-				'<a href="./api/files.ssjs?call=download-file&amp;dir=%s&amp;file=%s" target="_blank" class="list-group-item striped">' +
-				'<strong>%s</strong> (%s)' +
-				'<p><em>Uploaded %s</em></p>' +
-				'%s' +
-				'</a>',
-				http_request.query.dir[0], file.name, file.name, file.size,
-				system.timestr(file.uldate), description
-			)
-		);
-	}
-
-	writeln('<div id="file-list-container" class="list-group">');
-	listFiles(http_request.query.dir[0]).forEach(writeFileDetails);
-	writeln('</div>');
-
-
-} else if(
-	typeof http_request.query.library !== 'undefined' &&
-	typeof file_area.lib_list[http_request.query.library[0]] !== 'undefined'
-) {
-	// File directory listing
-
-	writeln(
-		'<ol class="breadcrumb">' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
-		'</li>' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '&amp;library=' + http_request.query.library[0] + '">' + file_area.lib_list[http_request.query.library[0]].name + '</a>' +
-		'</li>' +
-		'</ol>'
-	);
-
-	function writeDirectory(dir) {
-		writeln(
-			format(
-				'<a href="./?page=%s&amp;dir=%s" class="list-group-item striped">' +
-				'<h4><strong>%s</strong></h4>' +
-				'<p>%s : %s files</p>' +
-				'</a>',
-				http_request.query.page[0],
-				dir.dir.code,
-				dir.dir.name,
-				dir.dir.description,
-				dir.fileCount
-			)
-		);
-	}
-
-	writeln('<div id="file-list-container" class="list-group">');
-	listDirectories(http_request.query.library[0]).forEach(writeDirectory);
-	writeln('</div>');	
-
-} else {
-	// File library listing
-
-	writeln(
-		'<ol class="breadcrumb">' +
-		'<li>' +
-		'<a href="./?page=' + http_request.query.page[0] + '">Files</a>' +
-		'</li>' +
-		'</ol>'
-	);
-
-	function writeLibrary(library) {
-		writeln(
-			format(
-				'<a href="./?page=%s&amp;library=%s" class="list-group-item striped">' +
-				'<h3><strong>%s</strong></h3>' +
-				'<p>%s : %s directories</p>' +
-				'</a>',
-				http_request.query.page[0],
-				library.index,
-				library.name,
-				library.description,
-				library.dir_list.length
-			)
-		);
-	}
-
-	writeln('<div id="file-list-container" class="list-group">');
-	listLibraries().forEach(writeLibrary);
-	writeln('</div>');
-
-}
\ No newline at end of file
diff --git a/web/root/pages/002-files.xjs b/web/root/pages/002-files.xjs
new file mode 100644
index 0000000000..7022501f4f
--- /dev/null
+++ b/web/root/pages/002-files.xjs
@@ -0,0 +1,100 @@
+<!--HIDDEN:Files-->
+
+<?xjs
+	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+	load(system.exec_dir + '../web/lib/init.js');
+	load(settings.web_lib + 'files.js');
+
+	if (typeof http_request.query.dir !== 'undefined' &&
+		typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
+	) {
+?>
+	<ol class="breadcrumb">
+		<li>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">Files</a>
+		</li>
+		<li>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(file_area.dir[http_request.query.dir[0]].lib_index); ?>">
+				<?xjs write(file_area.dir[http_request.query.dir[0]].lib_name); ?>
+			</a>
+		</li>
+		<li>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;dir=<?xjs write(http_request.query.dir[0]); ?>">
+				<?xjs write(http_request.query.dir[0]); ?>
+			</a>
+		</li>
+	</ol>
+
+	<?xjs function writeFileDetails(file) { ?>
+		<a href="./api/files.ssjs?call=download-file&amp;dir=<?xjs write(http_request.query.dir[0]); ?>&amp;file=<?xjs write(file.name); ?>" target="_blank" class="list-group-item striped">
+			<strong><?xjs write(file.name); ?></strong> (<?xjs write(file.size); ?>)
+			<p><em>Uploaded <?xjs write(system.timestr(file.uldate)); ?></em></p>
+			<?xjs if (typeof file.extdesc === 'undefined') { ?>
+				<p><?xjs write(file.desc); ?></p>
+			<?xjs } else if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) { ?>
+				<pre class="ansi">
+					<?xjs write(html_encode(file.extdesc, true, false, true, true)); ?>
+				</pre>
+			<?xjs } else { ?>
+				<pre class="list">
+					<?xjs write(file.extdesc.replace(/[^\r,\n\x20-\x7E]/g, '')); ?>
+				</pre>
+			<?xjs } ?>
+		</a>
+	<?xjs } ?>
+
+	<div id="file-list-container" class="list-group">
+		<?xjs listFiles(http_request.query.dir[0]).forEach(writeFileDetails); ?>
+	</div>
+
+
+<?xjs
+	} else if(
+		typeof http_request.query.library !== 'undefined' &&
+		typeof file_area.lib_list[http_request.query.library[0]] !== 'undefined'
+	) {
+?>
+
+	<ol class="breadcrumb">
+		<li>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">Files</a>
+		</li>
+		<li>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(http_request.query.library[0]); ?>">
+				<?xjs write(file_area.lib_list[http_request.query.library[0]].name); ?>
+			</a>
+		</li>
+	</ol>
+
+
+	<?xjs function writeDirectory(dir) { ?>
+		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;dir=<?xjs write(dir.dir.code); ?>" class="list-group-item striped">
+			<h4><strong><?xjs write(dir.dir.name); ?></strong></h4>
+			<p><?xjs write(dir.dir.description); ?> : <?xjs write(dir.fileCount); ?> files</p>
+		</a>
+	<?xjs } ?>
+
+	<div id="file-list-container" class="list-group">
+		<?xjs listDirectories(http_request.query.library[0]).forEach(writeDirectory); ?>
+	</div>
+
+<?xjs } else { ?>
+
+	<ol class="breadcrumb">
+		<li>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">Files</a>
+		</li>
+	</ol>
+
+	<?xjs function writeLibrary(library) { ?>
+		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(library.index); ?>" class="list-group-item striped">
+			<h3><strong><?xjs write(library.name); ?></strong></h3>
+			<p><?xjs write(library.description); ?>: <?xjs write(library.dir_list.length); ?> directories</p>
+		</a>
+	<?xjs } ?>
+
+	<div id="file-list-container" class="list-group">
+		<?xjs listLibraries().forEach(writeLibrary); ?>
+	</div>
+
+<?xjs } ?>
\ No newline at end of file
-- 
GitLab


From c6cc18a43e8323b7f2f7a9a6de0883a1aa2e5d8a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 16:11:16 -0500
Subject: [PATCH 239/752] Removed HIDDEN flag.

---
 web/root/pages/002-files.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/002-files.xjs b/web/root/pages/002-files.xjs
index 7022501f4f..e98e0f0bc8 100644
--- a/web/root/pages/002-files.xjs
+++ b/web/root/pages/002-files.xjs
@@ -1,4 +1,4 @@
-<!--HIDDEN:Files-->
+<!--Files-->
 
 <?xjs
 	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
-- 
GitLab


From 3da77a903e90e4802eacca352c7d4cbbe16500bf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 16:23:52 -0500
Subject: [PATCH 240/752] Removed test garbage.

---
 web/root/index.xjs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 95cf8c535f..4df6de1b45 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -210,8 +210,6 @@
 		);
 	</script>
 
-	<?xjs if (user.alias === 'echicken') write(JSON.stringify(getPageList(settings.web_root + 'pages/'))); ?>
-
 	</body>
 
 </html>
\ No newline at end of file
-- 
GitLab


From 18c7a134fb9bba7137ace16329f3ed5f5e7da709 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 16:39:03 -0500
Subject: [PATCH 241/752] New layout settings in ctrl/modopts.ini [web]
 section; keys and defaults shown below:

layout_sidebar_off=false
layout_sidebar_left=false
layout_full_width=false

Set layout_sidebar_off to true to disable the sidebar altogether.
Set layout_sidebar_left to true to show the sidebar on the left side of the page.
Set layout_full_width to true to have the page contents expand to the width of the browser window.
---
 web/root/index.xjs | 91 ++++++++++++++++++++++++----------------------
 1 file changed, 48 insertions(+), 43 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 4df6de1b45..05699dbb0e 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -32,6 +32,46 @@
 
 ?>
 
+<?xjs function _subMenu(obj, title, path) { ?>
+	<li class="dropdown<?xjs if (path.match(/\//g).length > 1) write('-submenu'); ?>">
+		<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+			<?xjs write(title.replace(/^.*\-/, '')); ?>
+			<span class="caret"></span>
+		</a>
+		<ul class="dropdown-menu">
+			<?xjs _menu(obj, path); ?>
+		</ul>
+	</li>
+<?xjs } ?>
+
+<?xjs
+	function _menu(obj, path) {
+		Object.keys(obj).forEach(
+			function (e) {
+				if (typeof obj[e] === 'object') {
+					_subMenu(obj[e], e, (path || '') + e + '/');
+				} else {
+?>
+				<li>
+					<a href="./?page=<?xjs write((path || '') + obj[e]); ?>">
+						<?xjs write(e); ?>
+					</a>
+				</li>
+<?xjs
+				}
+			}
+		);
+	}
+?>
+
+<?xjs function _sidebar() {
+		if (settings.layout_sidebar_off) return;
+?>
+	<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
+		<?xjs writeSidebarModules(); ?>
+	</div>
+<?xjs } ?>
+
 <!DOCTYPE html>
 <html lang="en">
 	<head>
@@ -77,7 +117,7 @@
 		</div>
 
 		<nav class="navbar navbar-default navbar-fixed-top">
-			<div class="container">
+			<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
 				<div class="navbar-header">
 					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
 						<span class="sr-only">Sidebar</span>
@@ -90,43 +130,9 @@
 					</a>
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
-
-					<?xjs function subMenu(obj, title, path) { ?>
-						<li class="dropdown<?xjs if (path.match(/\//g).length > 1) write('-submenu'); ?>">
-							<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-								<?xjs write(title.replace(/^.*\-/, '')); ?>
-								<span class="caret"></span>
-							</a>
-							<ul class="dropdown-menu">
-								<?xjs menu(obj, path); ?>
-							</ul>
-						</li>
-					<?xjs } ?>
-
-					<?xjs
-						function menu(obj, path) {
-							Object.keys(obj).forEach(
-								function (e) {
-									if (typeof obj[e] === 'object') {
-										subMenu(obj[e], e, (path || '') + e + '/');
-									} else {
-					?>
-									<li>
-										<a href="./?page=<?xjs write((path || '') + obj[e]); ?>">
-											<?xjs write(e); ?>
-										</a>
-									</li>
-					<?xjs
-									}
-								}
-							);
-						}
-					?>
-
 					<ul class="nav navbar-nav">
-						<?xjs menu(getPageList(settings.web_root + 'pages/')); ?>
+						<?xjs _menu(getPageList(settings.web_root + 'pages/')); ?>
 					</ul>
-
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
 							<?xjs if (settings.user_registration) { ?>
@@ -173,19 +179,18 @@
 			</div>
 		</nav>
 
-		<div class="container">
-			<div class="row row-offcanvas row-offcanvas-right">
-				<div class="col-xs-12 col-sm-9">
-					<p class="pull-right visible-xs">
+		<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
+			<div class="row row-offcanvas row-offcanvas-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?>">
+				<?xjs if (settings.layout_sidebar_left) _sidebar(); ?>
+				<div class="col-xs-12 col-sm-<?xjs write(settings.layout_sidebar_off ? 12 : 9); ?>">
+					<p class="pull-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?> visible-xs">
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
 							<span class="glyphicon glyphicon-tasks"></span> Sidebar
 						</button>
 					</p>
 					<?xjs writePage(); ?>
 				</div>
-				<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
-					<?xjs writeSidebarModules(); ?>
-				</div>
+				<?xjs if (!settings.layout_sidebar_left || settings.layout_sidebar_right) _sidebar(); ?>
 			</div>
 		  	<hr>
 			<footer>
-- 
GitLab


From eb7edd36e2dc70eed7fc5dbc5996494f9fafb048 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 16:49:16 -0500
Subject: [PATCH 242/752] Removed more testing garbage.

---
 web/root/index.xjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 05699dbb0e..9dc4ace364 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -80,7 +80,6 @@
 		<meta name="viewport" content="width=device-width, initial-scale=1">
 		<link rel="icon" href="./images/favicon.ico">
 		<title>
-			<?xjs log(LOG_ERR, 'PAGE: ' + page); ?>
 			<?xjs write(getPageTitle(settings.web_root + 'pages/' + page).replace(/^HIDDEN(\:)*/, '')); ?>
 			:
 			<?xjs write(system.name); ?>
-- 
GitLab


From 601fa4a63b179e13bf6b532ae35c93e2c598430c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 17:48:01 -0500
Subject: [PATCH 243/752] New "control line" format (first comment line in
 SSJS,XJS,HTML file). Should be backwards-compatible with previous title-line
 format. Everything following the first colon on the first comment line is
 treated as the page title. If there is no colon, the entire line is treated
 as the page title. A pipe-delimited list of options can be specified before
 the colon. Available options at the moment are HIDDEN and NO_SIDEBAR. HIDDEN
 pages as always won't show up in the menus or in the who's online list. Set
 NO_SIDEBAR to disable the sidebar for this page; main content will fill the
 container.

---
 web/lib/pages.js   | 114 +++++++++++++++++++++++++--------------------
 web/root/index.xjs |   8 ++--
 2 files changed, 68 insertions(+), 54 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 6e2f236648..6f3c9bfb61 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -25,6 +25,63 @@ function webCtrlTest(ini, filename) {
 	return ret;
 }
 
+function getCtrlLine(file) {
+
+	if (backslash(
+			fullpath(file)
+		).indexOf(
+			backslash(fullpath(settings.web_root + '/pages'))
+		) !== 0
+	) {
+		return;
+	}
+
+	var f = new File(file);
+	var ctrl = '';
+	var ext = file_getext(file).toUpperCase();
+	switch (ext) {
+		case '.JS':
+		case '.SSJS':
+			if (!f.open('r')) return;
+			ctrl = f.readln().replace(/\/\//g, '');
+			f.close();
+			break;
+		case '.XJS':
+		case '.HTML':
+			if (!f.open('r')) return;
+			while (!f.eof) {
+				var i = f.readln().match(/\<\!\-\-(.*?)\-\-\>/);
+				if (i !== null && i.length > 1) {
+					ctrl = i[1];
+					break;
+				}
+			}
+			f.close();
+			break;
+		default:
+			ctrl = file_getname(file);
+			break;
+	}
+
+	var opts = ctrl.match(/^(.*?)\:/);
+	if (opts === null || opts.length < 2) {
+		opts = [];
+	} else {
+		opts = opts[1].toUpperCase().split('|');
+	}
+
+	var title = ctrl.replace(/^.*\:/, '');
+
+	return {
+		options : {
+			hidden : (opts.indexOf('HIDDEN') >= 0),
+			no_sidebar : (opts.indexOf('NO_SIDEBAR') >= 0)
+		},
+		title : title
+	};
+
+}
+
 function getPageList(dir) {
 
 	dir = backslash(fullpath(dir));
@@ -44,12 +101,12 @@ function getPageList(dir) {
 				}
 			} else if (e.search(/(\.xjs\.ssjs|webctrl\.ini)$/i) < 0) {
 				var fn = file_getname(e);
-				var title = getPageTitle(e);
+				var ctrl = getCtrlLine(e);
 				if ((	typeof webctrl === 'undefined' ||
 						webCtrlTest(webctrl, fn)
-					) && title.search(/^HIDDEN/) < 0
+					) && !ctrl.options.hidden
 				) {
-					pages[title] = file_getname(e);
+					pages[ctrl.title] = file_getname(e);
 				}
 			}
 		}
@@ -59,47 +116,6 @@ function getPageList(dir) {
 
 }
 
-function getPageTitle(file) {
-
-	if (backslash(
-			fullpath(file)
-		).indexOf(
-			backslash(fullpath(settings.web_root + '/pages'))
-		) !== 0
-	) {
-		return;
-	}
-
-	var title;
-	var ext = file_getext(file).toUpperCase();
-	var f = new File(file);
-	if (ext === '.JS' ||
-		(ext === '.SSJS' && file.search(/\.xjs\.ssjs$/i) === -1)
-	) {
-		f.open('r');
-		var i = f.readln();
-		f.close();
-		title = i.replace(/\/\//g, '');
-	} else if (ext === '.HTML' || ext === '.XJS') {
-		// Seek first comment line in an HTML document
-		f.open('r');
-		while (!f.eof) {
-			var i = f.readln();
-			var k = i.match(/^\<\!\-\-.*\-\-\>$/);
-			if (k !== null) {
-				title = k[0].replace(/[\<\!\-+|\-+\>]/g, '');
-				break;
-			}
-		}
-		f.close();
-	} else if (ext === '.TXT') {
-		title = file_getname(file);
-	}
-
-	return title;
-
-}
-
 function getPage(page) {
 
 	var ret = '';
@@ -111,13 +127,9 @@ function getPage(page) {
 	var ext = file_getext(page).toUpperCase();
 
 	if (user.alias != settings.guest) {
-		var title = getPageTitle(page);
-		if (title != 'HIDDEN') {
-			setSessionValue(
-				user.number,
-				'action',
-				title.replace(/^HIDDEN(\:)+/, '')
-			);
+		var ctrl = getCtrlLine(page);
+		if (typeof ctrl !== 'undefined' && !ctrl.options.hidden) {
+			setSessionValue(user.number, 'action', ctrl.title);
 		}
 	}
 
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 9dc4ace364..807a2a2b8b 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -21,6 +21,8 @@
 		: http_request.query.page[0]
 	);
 
+	var page_ctrl = getCtrlLine(settings.web_root + 'pages/' + page);
+
 	function writePage() {
 		var ini = getWebCtrl(
 			settings.web_root + 'pages/' + page.replace(file_getname(page), '')
@@ -65,7 +67,7 @@
 ?>
 
 <?xjs function _sidebar() {
-		if (settings.layout_sidebar_off) return;
+		if (settings.layout_sidebar_off || page_ctrl.options.no_sidebar) return;
 ?>
 	<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
 		<?xjs writeSidebarModules(); ?>
@@ -80,7 +82,7 @@
 		<meta name="viewport" content="width=device-width, initial-scale=1">
 		<link rel="icon" href="./images/favicon.ico">
 		<title>
-			<?xjs write(getPageTitle(settings.web_root + 'pages/' + page).replace(/^HIDDEN(\:)*/, '')); ?>
+			<?xjs write(page_ctrl.title); ?>
 			:
 			<?xjs write(system.name); ?>
 		</title>
@@ -181,7 +183,7 @@
 		<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
 			<div class="row row-offcanvas row-offcanvas-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?>">
 				<?xjs if (settings.layout_sidebar_left) _sidebar(); ?>
-				<div class="col-xs-12 col-sm-<?xjs write(settings.layout_sidebar_off ? 12 : 9); ?>">
+				<div class="col-xs-12 col-sm-<?xjs write(settings.layout_sidebar_off || page_ctrl.options.no_sidebar ? 12 : 9); ?>">
 					<p class="pull-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?> visible-xs">
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
 							<span class="glyphicon glyphicon-tasks"></span> Sidebar
-- 
GitLab


From 188e484e4dcb92f327aebb16c002b326199faf35 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 20:00:51 -0500
Subject: [PATCH 244/752] Documented sidebar modules, pages, page control
 lines, and the new layout settings.

---
 README.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 74 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 481184b41b..1635fa6fd0 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,12 @@ A web interface for Synchronet BBS
 	refresh_interval = 60000
 	; External Programs (or entire sections) to exclude from the Games page
 	xtrn_blacklist = scfg,oneliner
+	; Disable the sidebar altogether
+	layout_sidebar_off = false
+	; Place the sidebar on the left-hand side of the page
+	layout_sidebar_left = false
+	; Make the page consume the entire width of the browser window
+	layout_full_width = false
 ```
 - Add the following section to your *ctrl/services.ini* file:
 ```ini
@@ -102,7 +108,7 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 
 ####Additional / advanced settings
 
-- The following *optional* settings can be added to the [web] section of your *ctrl/modopts.ini* file:
+The following *optional* settings can be added to the [web] section of your *ctrl/modopts.ini* file:
 
 ```ini
 	; Use extended-ASCII characters when displaying forum messages (default: true)
@@ -120,14 +126,76 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 
 ###Customization
 
-- This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
-	- You can place your own CSS overrides in *web/root/css/custom.css*
-	- You can load another stylesheet of your own choosing in the &lt;head&gt; section of *web/root/index.xjs* (load it after the others)
-- The sidebar module & page structure is *mostly* similar to the system used in ecWeb v3.  See [the old instructions](http://wiki.synchro.net/howto:ecweb#the_sidebar) for info on adding content.
+This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
+
+- You can place your own CSS overrides in *web/root/css/custom.css*.  Create this file and use it if you want to use fonts, colours, etc. other than the defaults.  It is not recommended that you modify any of the existing stylesheets.
+
+####Sidebar Modules
+
+Sidebar modules are the widgets displayed in the narrow column running down the right (or left) side of the page.  A sidebar module can be an SSJS, XJS, HTML, or TXT file.
+
+- Sidebar modules are loaded in alphanumeric order from the *web/root/sidebar/* directory; see the included files for examples and for a file-naming convention that enforces order of appearance
+- HTML, XJS, and SSJS sidebar modules should not be complete HTML documents.  They should not have <html>, <head>, or <body> tags.  Instead, they should contain (or produce) an HTML snippet suitable for inclusion in the overall page.
+- TXT sidebar modules are displayed inside of <pre> tags to preserve fixed-width formatting.
+- Support for additional file formats can be added if necessary, but by using HTML and Javascript you should be able to display whatever you like.  (If you want to put an image in the sidebar, a simple HTML file containing an <img> tag will do the job, for example.)
+
+####Pages
+
+Like sidebar modules, pages can be HTML, XJS, SSJS, or TXT files.
+
+- Pages are loaded in alphanumeric order from the *web/root/pages/* directory.  See the included files for examples and for a file-naming convention that enforces order of appearance.
+- As with sidebar modules, HTML, XJS, and SSJS pages should not be complete HTML documents.  They should contain (or generate) snippets of text or HTML suitable for inclusion in the overall page.
+- Your *web/root/pages/* directory should contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file.
+	- You can use this file to restrict access to certain pages so that guests or unprivileged users can't see them
+- In an HTML, XJS, or SSJS file, the first line *containing a comment* is treated as the *control line* for the page.
+	- The format of the control line is *OPTION|OPTION:Title*
+	- Available options at the moment are *HIDDEN* and *NO_SIDEBAR*
+	- *HIDDEN* pages will not appear in the menus or in the activity fields of the *who's online* list
+	- The *NO_SIDEBAR* directive tells the layout script not to include a sidebar on this page
+		- The main content will expand to fill the space normally used by the sidebar
+	- The part of the control line following the first colon is treated as the title of the page.  This is the text that appears in the menu, browser title bar, and *who's online* list.
+		- If you don't need to specify any options, (and as long as your page title doesn't contain a colon) you can omit the colon, and the entire string will be treated as the title of the page, or you can start the control line with a colon.
+
+Here's an example control line for a hidden HTML file:
+
+```html
+<!--HIDDEN:My Awesome Web Page-->
+```
+
+Here's an example control file for a hidden XJS page with no sidebar:
+
+```html
+<!--HIDDEN|NO_SIDEBAR:My Awesome Web Page-->
+```
+
+Here's an example control file for an SSJS script with no settings:
+
+```js
+//My Awesome Web Page
+```
+
+If your page title contains a colon, it's necessary to prepend a dummy one like so:
+
+```js
+//:Awesome Web Pages: This is one
+```
+
+Of course, that's not an issue if you're providing some settings:
+
+```html
+<!--NO_SIDEBAR:Awesome Web Pages: This is one-->
+```
+
+You can add drop-down menus to the navigation bar by adding subdirectories to the *web/root/pages/* directory.  The files within these subdirectories follow the same format described above.
+- The name of the subdirectory is used as the text of the menu item.
+- You can nest additional subdirectories to create sub-menus.
+- Subdirectories with names beginning with *.* will be ignored.
+- Each subdirectory can contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file for access control.
 
 ###Uninstall
 
-- To stop using this web interface, you can just revert to your previous *web* directory at any time.
+To stop using this web interface, you can just revert to your previous *web* directory at any time.
+
 - The [web] section added to *ctrl/modopts.ini* won't hurt anything if you leave it there, but you can delete it if you want
 - Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
 - Undo any changes you made to your firewall & router during the *Quick Start*
-- 
GitLab


From 66c0b26abe5bbd1441ad96a60a9f4b61c90abb05 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 20:04:59 -0500
Subject: [PATCH 245/752] Derps.

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 1635fa6fd0..c3e005ffde 100644
--- a/README.md
+++ b/README.md
@@ -135,7 +135,7 @@ This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should
 Sidebar modules are the widgets displayed in the narrow column running down the right (or left) side of the page.  A sidebar module can be an SSJS, XJS, HTML, or TXT file.
 
 - Sidebar modules are loaded in alphanumeric order from the *web/root/sidebar/* directory; see the included files for examples and for a file-naming convention that enforces order of appearance
-- HTML, XJS, and SSJS sidebar modules should not be complete HTML documents.  They should not have <html>, <head>, or <body> tags.  Instead, they should contain (or produce) an HTML snippet suitable for inclusion in the overall page.
+- HTML, XJS, and SSJS sidebar modules should not be complete HTML documents.  They should not have \<html\>, \<head\>, or \<body\> tags.  Instead, they should contain (or produce) an HTML snippet suitable for inclusion in the overall page.
 - TXT sidebar modules are displayed inside of <pre> tags to preserve fixed-width formatting.
 - Support for additional file formats can be added if necessary, but by using HTML and Javascript you should be able to display whatever you like.  (If you want to put an image in the sidebar, a simple HTML file containing an <img> tag will do the job, for example.)
 
@@ -217,4 +217,4 @@ To stop using this web interface, you can just revert to your previous *web* dir
 
 ####Email
 
-- Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
\ No newline at end of file
+- Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
-- 
GitLab


From cb3bc044fcc9ac7a1dbbf971ca79d70c2482d032 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 20:06:15 -0500
Subject: [PATCH 246/752] More escaping of HTML.

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index c3e005ffde..8db1e160f3 100644
--- a/README.md
+++ b/README.md
@@ -136,8 +136,8 @@ Sidebar modules are the widgets displayed in the narrow column running down the
 
 - Sidebar modules are loaded in alphanumeric order from the *web/root/sidebar/* directory; see the included files for examples and for a file-naming convention that enforces order of appearance
 - HTML, XJS, and SSJS sidebar modules should not be complete HTML documents.  They should not have \<html\>, \<head\>, or \<body\> tags.  Instead, they should contain (or produce) an HTML snippet suitable for inclusion in the overall page.
-- TXT sidebar modules are displayed inside of <pre> tags to preserve fixed-width formatting.
-- Support for additional file formats can be added if necessary, but by using HTML and Javascript you should be able to display whatever you like.  (If you want to put an image in the sidebar, a simple HTML file containing an <img> tag will do the job, for example.)
+- TXT sidebar modules are displayed inside of \<pre\> tags to preserve fixed-width formatting.
+- Support for additional file formats can be added if necessary, but by using HTML and Javascript you should be able to display whatever you like.  (If you want to put an image in the sidebar, a simple HTML file containing an \<img\> tag will do the job, for example.)
 
 ####Pages
 
-- 
GitLab


From 3416bb7aaf12015a04e5c0e7cadb8121a64e1dfa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 22:50:21 -0500
Subject: [PATCH 247/752] Load [WebSocketTelnet] section from services.ini (per
 instructions, whoops); if that fails, try [WebSocket].

---
 web/lib/ftelnet.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index 9957af8474..0cf3262471 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -1,6 +1,7 @@
 var f = new File(file_cfgname(system.ctrl_dir, "services.ini"));
 if (!f.open('r')) exit();
-var webSocket = f.iniGetObject('WebSocket');
+var webSocket = f.iniGetObject('WebSocketTelnet');
+if (webSocket === null) webSocket = f.iniGetObject('WebSocket');
 var webSocketRLogin = f.iniGetObject('WebSocketRLogin');
 f.close();
 
-- 
GitLab


From 8b95182049f2ab879dbf31b0082e3a63a832f29b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 9 Jan 2017 23:37:45 -0500
Subject: [PATCH 248/752] More Winders-friendly directory traversal prevention
 and path sanitizing.  This and the previous commit should resolve problems
 seen by kk4qbn.

---
 web/lib/pages.js   | 8 ++------
 web/root/index.xjs | 2 +-
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 6f3c9bfb61..53c9299695 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -27,12 +27,7 @@ function webCtrlTest(ini, filename) {
 
 function getCtrlLine(file) {
 
-	if (backslash(
-			fullpath(file)
-		).indexOf(
-			backslash(fullpath(settings.web_root + '/pages'))
-		) !== 0
-	) {
+	if (fullpath(file).indexOf(fullpath(settings.web_root + '/pages')) !== 0) {
 		return;
 	}
 
@@ -95,6 +90,7 @@ function getPageList(dir) {
 	directory(dir + '*').forEach(
 		function (e) {
 			if (file_isdir(e)) {
+				e = fullpath(e);
 				var list = getPageList(e);
 				if (Object.keys(list).length > 0) {
 					pages[e.split(e.substr(-1, 1)).slice(-2, -1)] = list;
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 807a2a2b8b..4a91eef8c4 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -15,7 +15,7 @@
 			) ||
 			fullpath(
 				settings.web_root + 'pages/' + http_request.query.page[0]
-			).indexOf(settings.web_root + 'pages') !== 0
+			).indexOf(fullpath(settings.web_root + 'pages')) !== 0
 		)
 		? '000-home.xjs'
 		: http_request.query.page[0]
-- 
GitLab


From e84ab7621394584a4bdf2092acdacf12d6c37cd7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 00:20:22 -0500
Subject: [PATCH 249/752] Typos

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 8db1e160f3..520f3a2dd7 100644
--- a/README.md
+++ b/README.md
@@ -162,13 +162,13 @@ Here's an example control line for a hidden HTML file:
 <!--HIDDEN:My Awesome Web Page-->
 ```
 
-Here's an example control file for a hidden XJS page with no sidebar:
+Here's an example control line for a hidden XJS page with no sidebar:
 
 ```html
 <!--HIDDEN|NO_SIDEBAR:My Awesome Web Page-->
 ```
 
-Here's an example control file for an SSJS script with no settings:
+Here's an example control line for an SSJS script with no settings:
 
 ```js
 //My Awesome Web Page
-- 
GitLab


From a887d6b7bdecbc10b54a98a0be4671401eeb7c41 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 12:45:04 -0500
Subject: [PATCH 250/752] Default language file and loading method.

---
 web/lib/language.js          | 17 ++++++++++++++++
 web/lib/language/english.ini | 38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)
 create mode 100644 web/lib/language.js
 create mode 100644 web/lib/language/english.ini

diff --git a/web/lib/language.js b/web/lib/language.js
new file mode 100644
index 0000000000..9e0de939d7
--- /dev/null
+++ b/web/lib/language.js
@@ -0,0 +1,17 @@
+function getLanguage(file, section) {
+
+	var stock = 'english.ini';
+
+	if (!file_exists(settings.web_lib + 'language/' + file)) {
+		return getLanguage(stock, section);
+	}
+
+	var f = new File(settings.web_lib + 'language/' + file);
+	if (!f.open('r')) throw 'Unable to open language file ' + file;
+	var ini = f.iniGetObject(section);
+	f.close();
+	if (ini === null && file !== stock) ini = getLanguage(stock, section);
+
+	return ini;
+
+}
\ No newline at end of file
diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
new file mode 100644
index 0000000000..7200ba2a26
--- /dev/null
+++ b/web/lib/language/english.ini
@@ -0,0 +1,38 @@
+[main]
+button_close = Close
+button_submit = Submit
+button_login = Log in
+input_username = Username
+input_password = Password
+label_sidebar = Sidebar
+label_unread_mail = Unread mail
+menu_item_login = Log in
+menu_item_logout = Log out
+menu_item_mail = Mail
+menu_item_register = Register
+
+[home]
+button_ftelnet = Connect via Telnet
+
+[node_list]
+label_title = Who's Online
+label_node_column = Node
+label_send_telegram = Send a telegram
+label_status_column = Status
+
+[system_info]
+label_title = System Info
+label_sysop = Sysop:
+label_location = Location:
+label_users = Users:
+label_nodes = Nodes:
+label_uptime = Uptime:
+label_calls_total = Calls:
+label_calls_today = Calls today:
+label_files_total = Files:
+label_files_uploaded_today = U/L today:
+label_files_downloaded_today = D/L today:
+label_messages_total = Messages:
+label_messages_posted_today = Posted today:
+stat_suffix_files = files
+stat_suffix_bytes = bytes
\ No newline at end of file
-- 
GitLab


From 924153a5b7824bc4b47a957ab9a20b36eb9ce113 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 12:49:38 -0500
Subject: [PATCH 251/752] Use strings from language file rather than hard-coded
 enlish lanuage perthetic looser talk.

---
 web/root/index.xjs                   | 47 +++++++++++++++++++---------
 web/root/sidebar/001-nodelist.xjs    |  8 +++--
 web/root/sidebar/003-systemStats.xjs | 44 ++++++++++++++++----------
 3 files changed, 65 insertions(+), 34 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 4a91eef8c4..26993ff1f3 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -5,6 +5,9 @@
 	load(settings.web_lib + 'auth.js');
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
+	load(settings.web_lib + 'language.js');
+
+	var _language = getLanguage(settings.language_file || 'english.ini', 'main');
 
 	var page = (
 		(	typeof http_request.query.page === 'undefined' ||
@@ -110,8 +113,12 @@
 					</div>
 					<div class="modal-body" id="popUpModalBody"></div>
 					<div class="modal-footer">
-						<button type="button" class="btn btn-default" id="popUpModalCloseButton">Close</button>
-						<button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>Submit</button>
+						<button type="button" class="btn btn-default" id="popUpModalCloseButton">
+							<?xjs write(_language.button_close); ?>
+						</button>
+						<button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>
+							<?xjs write(_language.button_submit); ?>
+						</button>
 					</div>
 				</div>
 			</div>
@@ -121,7 +128,9 @@
 			<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
 				<div class="navbar-header">
 					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
-						<span class="sr-only">Sidebar</span>
+						<span class="sr-only">
+							<?xjs write(_language.sidebar_label); ?>
+						</span>
 						<span class="icon-bar"></span>
 						<span class="icon-bar"></span>
 						<span class="icon-bar"></span>
@@ -138,20 +147,27 @@
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
 							<?xjs if (settings.user_registration) { ?>
 								<li>
-									<a href="./?page=000-register.xjs">Register</a>
+									<a href="./?page=000-register.xjs">
+										<?xjs write(_language.menu_item_register); ?>
+									</a>
 								</li>
 							<?xjs } ?>
 							<li class="dropdown">
 								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-									Log in<span class="caret"></span>
+									<?xjs write(_language.menu_item_login); ?>
+									<span class="caret"></span>
 								</a>
 								<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
 									<form id="form-login">
-										<label for="input-username" class="sr-only">Username</label>
-										<input id="input-username" title="Username" type="text" class="dropdown form-control" placeholder="Username">
-										<label for="input-password" class="sr-only">Password</label>
-										<input id="input-password" title="Password" type="password" class="dropdown form-control" placeholder="Password">
-										<input id="button-login" class="dropdown btn btn-primary" type="submit" value="Log in">
+										<label for="input-username" class="sr-only">
+											<?xjs write(_language.input_username); ?>
+										</label>
+										<input id="input-username" title="<?xjs write(_language.input_username); ?>" type="text" class="dropdown form-control" placeholder="<?xjs write(_language.input_username); ?>">
+										<label for="input-password" class="sr-only">
+											<?xjs write(_language.input_password); ?>
+										</label>
+										<input id="input-password" title="<?xjs write(_language.input_password); ?>" type="password" class="dropdown form-control" placeholder="<?xjs write(_language.input_password); ?>">
+										<input id="button-login" class="dropdown btn btn-primary" type="submit" value="<?xjs write(_language.button_login); ?>">
 									</form>
 								</div>
 							</li>
@@ -165,12 +181,14 @@
 								<ul class="dropdown-menu">
 									<li>
 										<a href="./?page=000-mail.xjs">
-											Mail
-											<span class="badge scanned" title="Unread mail" id="badge-unread-mail-inner"></span>
+											<?xjs write(_language.menu_item_mail); ?>
+											<span class="badge scanned" title="<?xjs write(_language.label_unread_mail); ?>" id="badge-unread-mail-inner"></span>
 										</a>
 									</li>
 									<li>
-										<a id="button-logout" href="#">Log out</a>
+										<a id="button-logout" href="#">
+											<?xjs write(_language.menu_item_logout); ?>
+										</a>
 									</li>
 								</ul>
 							</li>
@@ -186,7 +204,8 @@
 				<div class="col-xs-12 col-sm-<?xjs write(settings.layout_sidebar_off || page_ctrl.options.no_sidebar ? 12 : 9); ?>">
 					<p class="pull-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?> visible-xs">
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
-							<span class="glyphicon glyphicon-tasks"></span> Sidebar
+							<span class="glyphicon glyphicon-tasks"></span>
+							<?xjs write(_language.sidebar_label); ?>
 						</button>
 					</p>
 					<?xjs writePage(); ?>
diff --git a/web/root/sidebar/001-nodelist.xjs b/web/root/sidebar/001-nodelist.xjs
index a798e2b0ac..c65f907b71 100644
--- a/web/root/sidebar/001-nodelist.xjs
+++ b/web/root/sidebar/001-nodelist.xjs
@@ -1,3 +1,5 @@
+<?xjs var _nll = getLanguage(settings.language_file || 'english.ini', 'node_list'); ?>
+
 <div id="sbbs-nodelist"></div>
 <script type="text/javascript">
 var nodeList = function() {
@@ -5,10 +7,10 @@ var nodeList = function() {
 		'./api/system.ssjs?call=node-list',
 		function (data) {
 			$('#sbbs-nodelist').html(
-				'<h4>Who\'s online</h4>' +
+				"<h4><?xjs write(_nll.label_title); ?></h4>" +
 				'<table id="sbbs-nodelist-table" ' +
 				'class="table table-condensed table-responsive table-striped">'+
-				'<thead><tr><th>Node</th><th>Status</th></tr></thead>' +
+				'<thead><tr><th><?xjs write(_nll.label_node_column); ?></th><th><?xjs write(_nll.label_status_column); ?></th></tr></thead>' +
 				'<tbody></tbody>' +
 				'</table>'
 			);
@@ -33,7 +35,7 @@ var nodeList = function() {
 					) {
 						$('#nodelist-' + index).attr(
 							'title',
-							'Send a telegram'
+							'<?xjs write(_nll.label_send_telegram); ?>'
 						);
 						$('#nodelist-' + index).css('cursor', 'pointer');
 						$('#nodelist-' + index).click(
diff --git a/web/root/sidebar/003-systemStats.xjs b/web/root/sidebar/003-systemStats.xjs
index 2fac32bfae..803599779e 100644
--- a/web/root/sidebar/003-systemStats.xjs
+++ b/web/root/sidebar/003-systemStats.xjs
@@ -1,57 +1,67 @@
+<?xjs var _sill = getLanguage(settings.language_file || 'english.ini', 'system_info'); ?>
+
 <h4>System Info</h4>
 <table class="table table-condensed table-responsive table-striped">
 	<tbody>
 		<tr>
-			<th>Sysop:</th>
+			<th scope="row"><?xjs write(_sill.label_sysop); ?></th>
 			<td><?xjs write(system.operator); ?></td>
 		</tr>
 		<tr>
-			<th>Location:</th>
+			<th scope="row"><?xjs write(_sill.label_location); ?></th>
 			<td><?xjs write(system.location); ?></td>
 		</tr>
 		<tr>
-			<th>Users:</th>
+			<th scope="row"><?xjs write(_sill.label_users); ?></th>
 			<td><?xjs write(system.stats.total_users); ?></td>
 		</tr>
 		<tr>
-			<th>Nodes:</th>
+			<th scope="row"><?xjs write(_sill.label_nodes); ?></th>
 			<td><?xjs write(system.nodes); ?></td>
 		</tr>
 		<tr>
-			<th>Uptime:</th>
+			<th scope="row"><?xjs write(_sill.label_uptime); ?></th>
 			<td><?xjs write(system.secondstr(time() - system.uptime)); ?></td>
 		</tr>
 		<tr>
-			<th>Calls:</th>
+			<th scope="row"><?xjs write(_sill.label_calls_total); ?></th>
 			<td><?xjs write(system.stats.total_logons); ?></td>
 		</tr>
 		<tr>
-			<th>Calls today:</th>
+			<th scope="row"><?xjs write(_sill.label_calls_today); ?></th>
 			<td><?xjs write(system.stats.logons_today); ?></td>
 		</tr>
 		<tr>
-			<th>Files:</th>
+			<th scope="row"><?xjs write(_sill.label_files_total); ?></th>
 			<td><?xjs write(system.stats.total_files); ?></td>
 		</tr>
 		<tr>
-			<th>U/L today:</th>
-			<td><?xjs write(system.stats.files_uploaded_today); ?> files<br>
-				(<?xjs write(system.stats.bytes_uploaded_today); ?> bytes)
+			<th scope="row"><?xjs write(_sill.label_files_uploaded_today); ?></th>
+			<td>
+				<?xjs write(system.stats.files_uploaded_today); ?> 
+				<?xjs write(_sill.stat_suffix_files); ?>
+				<br>
+				(<?xjs write(system.stats.bytes_uploaded_today); ?> 
+				<?xjs write(_sill.stat_suffix_bytes); ?>)
 			</td>
 		</tr>
 		<tr>
-			<th>D/L today:</th>
-			<td><?xjs write(system.stats.files_downloaded_today); ?> files<br>
-				(<?xjs write(system.stats.bytes_downloaded_today); ?> bytes)
+			<th scope="row"><?xjs write(_sill.label_files_downloaded_today); ?></th>
+			<td>
+				<?xjs write(system.stats.files_downloaded_today); ?> 
+				<?xjs write(_sill.stat_suffix_files); ?>
+				<br>
+				(<?xjs write(system.stats.bytes_downloaded_today); ?> 
+				<?xjs write(_sill.stat_suffix_bytes); ?>)
 			</td>
 		</tr>
 		<tr>
-			<th>Messages:</th>
+			<th scope="row"><?xjs write(_sill.label_messages_total); ?></th>
 			<td><?xjs write(system.stats.total_messages); ?></td>
 		</tr>
 		<tr>
-			<th>Posts today:</th>
+			<th scope="row"><?xjs write(_sill.label_messages_posted_today); ?></th>
 			<td><?xjs write(system.stats.messages_posted_today); ?></td>
 		</tr>
 	</tbody>
-</table>
+</table>
\ No newline at end of file
-- 
GitLab


From 0785f8aaeec2e6e44daffe4cac0ee01132156b6e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 12:52:53 -0500
Subject: [PATCH 252/752] Use button_ftelnet text from language file [home]
 section in place of previous 'Connect via Telnet' string.

---
 web/root/pages/000-home.xjs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index d3269a2900..687ed896cb 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -2,13 +2,16 @@
 <?xjs
 	if (typeof argv[0] != 'boolean' || !argv[0]) exit(); // I don't remember what this is for
 	load(settings.web_lib + 'ftelnet.js');
+	var _hpl = getLanguage(settings.language_file || 'english.ini', 'home'); ?>
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
 <div class="row">
 	<div class="center-block" style="width:200px;margin-bottom:1em;">
-		<button id="ftelnet-connect" class="btn btn-primary">Connect via Telnet</button>
+		<button id="ftelnet-connect" class="btn btn-primary">
+			<?xjs write(_hpl.button_ftelnet); ?>
+		</button>
 	</div>
 </div>
 <script type="text/javascript">
-- 
GitLab


From b413a7e09162cc8176dd4491a46369f4c580466a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 13:01:58 -0500
Subject: [PATCH 253/752] Replaced hard-coded english strings with references
 to loaded language file.

---
 web/root/pages/002-files.xjs | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/web/root/pages/002-files.xjs b/web/root/pages/002-files.xjs
index e98e0f0bc8..f8a37740c1 100644
--- a/web/root/pages/002-files.xjs
+++ b/web/root/pages/002-files.xjs
@@ -5,13 +5,17 @@
 	load(system.exec_dir + '../web/lib/init.js');
 	load(settings.web_lib + 'files.js');
 
+	var _fpl = getLanguage(settings.language_file || 'english.ini', 'files');
+
 	if (typeof http_request.query.dir !== 'undefined' &&
 		typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
 	) {
 ?>
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">Files</a>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
+				<?xjs write(_fpl.title); ?>
+			</a>
 		</li>
 		<li>
 			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(file_area.dir[http_request.query.dir[0]].lib_index); ?>">
@@ -57,7 +61,9 @@
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">Files</a>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
+				<?xjs write(_fpl.title); ?>
+			</a>
 		</li>
 		<li>
 			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(http_request.query.library[0]); ?>">
@@ -70,7 +76,11 @@
 	<?xjs function writeDirectory(dir) { ?>
 		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;dir=<?xjs write(dir.dir.code); ?>" class="list-group-item striped">
 			<h4><strong><?xjs write(dir.dir.name); ?></strong></h4>
-			<p><?xjs write(dir.dir.description); ?> : <?xjs write(dir.fileCount); ?> files</p>
+			<p>
+				<?xjs write(dir.dir.description); ?>: 
+				<?xjs write(dir.fileCount); ?> 
+				<?xjs write(dir.fileCount === 1 ? _fpl.stat_suffix_file : _fpl.stat_suffix_files); ?>
+			</p>
 		</a>
 	<?xjs } ?>
 
@@ -82,14 +92,20 @@
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">Files</a>
+			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
+				<?xjs write(_fpl.title); ?>
+			</a>
 		</li>
 	</ol>
 
 	<?xjs function writeLibrary(library) { ?>
 		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(library.index); ?>" class="list-group-item striped">
 			<h3><strong><?xjs write(library.name); ?></strong></h3>
-			<p><?xjs write(library.description); ?>: <?xjs write(library.dir_list.length); ?> directories</p>
+			<p>
+				<?xjs write(library.description); ?>: 
+				<?xjs write(library.dir_list.length); ?> 
+				<?xjs write(library.dir_list.length === 1 ? _fpl.stat_suffix_directory : _fpl.stat_suffix_directories); ?>
+			</p>
 		</a>
 	<?xjs } ?>
 
-- 
GitLab


From b9296bd6be86e237840280dabf6ad9dd68c2b214 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 13:12:36 -0500
Subject: [PATCH 254/752] More strings.

---
 web/lib/language/english.ini | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
index 7200ba2a26..e597c0b6d3 100644
--- a/web/lib/language/english.ini
+++ b/web/lib/language/english.ini
@@ -14,6 +14,24 @@ menu_item_register = Register
 [home]
 button_ftelnet = Connect via Telnet
 
+[mail]
+button_post_new = Post a new message
+button_select_all = Select all messages
+button_delete_selected = Delete selected messages
+label_message_from = From
+label_message_to = To
+label_message_date = on
+label_message_subject = Subject
+label_tab_inbox = Inbox
+label_tab_sent = Sent
+
+[files]
+title = Files
+stat_suffix_file = file
+stat_suffix_files = files
+stat_suffix_directory = directory
+stat_suffix_directories = directories
+
 [node_list]
 label_title = Who's Online
 label_node_column = Node
-- 
GitLab


From 4a4cb49430b700ac8f6533559126b96b15df30d3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 13:13:59 -0500
Subject: [PATCH 255/752] Use [mail] strings from language file.

---
 web/root/pages/000-mail.xjs | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/web/root/pages/000-mail.xjs b/web/root/pages/000-mail.xjs
index 0246f76a0b..9341d37f56 100644
--- a/web/root/pages/000-mail.xjs
+++ b/web/root/pages/000-mail.xjs
@@ -9,6 +9,8 @@ load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'forum.js');
 
+var _mpl = getLanguage(settings.language_file || 'english.ini', 'mail');
+
 function sent() {
 	if (typeof http_request.query.sent === 'undefined') return false;
 	if (http_request.query.sent[0] == '0') return false;
@@ -28,10 +30,14 @@ function sent() {
 				</div>
 			</div>
 			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
-				<?xjs write(!sent() ? 'From' : 'To'); ?>: 
+				<?xjs write(!sent() ? _mpl.label_message_from : _mpl.label_message_to); ?>: 
 				<strong><?xjs write(!sent() ? header.from : header.to); ?></strong> 
-				on <?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
-				<p>Subject: <strong><?xjs write(header.subject); ?></strong></p>
+				<?xjs write(_mpl.label_message_date); ?>
+				<?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
+				<p>
+					<?xjs write(_mpl.label_message_subject); ?>: 
+					<strong><?xjs write(header.subject); ?></strong>
+				</p>
 			</div>
 		</div>
 		<div class="message" id="message-<?xjs write(header.number); ?>" hidden></div>
@@ -76,23 +82,27 @@ function sent() {
 <?xjs } ?>
 
 <?xjs if (!(user.security.restrictions&UFLAG_E) && !(user.security.restrictions&UFLAG_M)) { ?>
-	<button class="btn btn-default icon" aria-label="Post a new message" title="Post a new message" onclick="addNew('mail')">
+	<button class="btn btn-default icon" aria-label="<?xjs write(_mpl.button_post_new); ?>" title="<?xjs write(_mpl.button_post_new); ?>" onclick="addNew('mail')">
 		<span class="glyphicon glyphicon-pencil"></span>
 	</button>
-	<button class="btn btn-default icon" aria-label="Select all messages" title="Select all messages" onclick="selectAllMail()">
+	<button class="btn btn-default icon" aria-label="<?xjs write(_mpl.button_select_all); ?>" title="<?xjs write(_mpl.button_select_all); ?>" onclick="selectAllMail()">
 		<span class="glyphicon glyphicon-check"></span>
 	</button>
-	<button class="btn btn-default icon" aria-label="Delete selected messages" title="Delete selected messages" onclick="deleteSelectedMail()">
+	<button class="btn btn-default icon" aria-label="<?xjs write(_mpl.button_delete_selected); ?>" title="<?xjs write(_mpl.button_delete_selected); ?>" onclick="deleteSelectedMail()">
 		<span class="glyphicon glyphicon-trash"></span>
 	</button>
 <?xjs } ?>
 
 <ul class="nav nav-tabs">
 	<li role="presentation" class="<?xjs write(!sent() ? ' active' : ''); ?>">
-		<a href="./?page=<?xjs write(page); ?>&amp;sent=0">Inbox</a>
+		<a href="./?page=<?xjs write(page); ?>&amp;sent=0">
+			<?xjs write(_mpl.label_tab_inbox); ?>
+		</a>
 	</li>
 	<li role="presentation" class="<?xjs write(sent() ? ' active' : ''); ?>">
-		<a href="./?page=<?xjs write(page); ?>&amp;sent=1">Sent</a>
+		<a href="./?page=<?xjs write(page); ?>&amp;sent=1">
+			<?xjs write(_mpl.label_tab_sent); ?>
+		</a>
 	</li>
 </ul>
 <br>
-- 
GitLab


From ee10f9c34ff62cca41be92552cf451e7fc1abbd1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 13:55:03 -0500
Subject: [PATCH 256/752] More strings

---
 web/lib/language/english.ini | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
index e597c0b6d3..60d9ab8ad7 100644
--- a/web/lib/language/english.ini
+++ b/web/lib/language/english.ini
@@ -25,6 +25,38 @@ label_message_subject = Subject
 label_tab_inbox = Inbox
 label_tab_sent = Sent
 
+[register]
+title = Register
+button_register = Register
+label_field_required = Required
+input_alias = Username
+input_password = Password
+input_password_confirm = Confirm password
+input_email = Email address
+input_name = Real name
+input_street_address = Street address
+input_zipcode = Postal code
+input_location = Location (City, State/Province)
+input_phone = Telephone number
+input_birthdate = Birthdate
+input_gender = Gender
+input_gender_withheld = Withheld
+input_gender_male = Male
+input_gender_female = Female
+input_gender_other = Other
+input_registration_password = Registration password
+stat_suffix_field_required = required
+help_text_required = Fields marked with an asterisk are required.  All others can be left blank if you wish.
+help_text_minimum_characters = Minimum of %d characters
+help_text_maximum_characters = Maximum of %d characters
+message_account_created = Your account has been created
+placeholder_netmail = pat@m.f
+placeholder_name = Pat Androgyne
+placeholder_street_address = 123 Any Street
+placeholder_zipcode = H0H0H0
+placeholder_location = City, State
+placeholder_phone = 800-555-5555
+
 [files]
 title = Files
 stat_suffix_file = file
-- 
GitLab


From 6243757b5785ef71a083b4f45873ce604cc41f01 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 13:55:45 -0500
Subject: [PATCH 257/752] Replaced hard-coded strings with references to loaded
 language section.

---
 web/root/pages/000-register.xjs | 128 +++++++++++++++++---------------
 1 file changed, 68 insertions(+), 60 deletions(-)

diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
index 11b031f169..1e166135cb 100644
--- a/web/root/pages/000-register.xjs
+++ b/web/root/pages/000-register.xjs
@@ -12,161 +12,167 @@
 
 	load('sbbsdefs.js');
 
+	var _rpl = getLanguage(settings.language_file || 'english.ini', 'register');
+
 	function required(mask) {
-		return ((system.new_user_questions&mask) ? ' required' : '');
+		return ((system.new_user_questions&mask) ? (' ' + _rpl.stat_suffix_field_required) : '');
 	}
 
 	function notRequired(mask) {
-		return ((system.new_user_questions&mask) ? '' : ' required');
+		return ((system.new_user_questions&mask) ? '' : (' ' + _rpl.stat_suffix_field_required));
 	}
 
+
 	function iconRequired(mask) {
 		if (required(mask).length > 0) {
-			write(
-				'<span title="Required" class="glyphicon glyphicon-asterisk">' +
-				'</span>'
-			);
+?>
+			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+<?xjs
 		}
 	}
 
 	function iconNotRequired(mask) {
 		if (notRequired(mask).length > 0) {
-			write(
-				'<span title="Required" class="glyphicon glyphicon-asterisk">' +
-				'</span>'
-			);
+?>
+			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+<?xjs
 		}
 	}
-
 ?>
 
 <script type="text/javascript" src="./js/validator.js"></script>
 
-<div class="well well-sm"><h3>Register</h3></div>
+<div class="well well-sm"><h3><?xjs write(_rpl.title); ?></h3></div>
 
 <div id="errorbox" class="bg-danger" hidden></div>
 
 <div id="form-register-container">
 
 	<div style="margin-bottom:1em;">
-		Fields marked with 
-		<span title="Required" class="glyphicon glyphicon-asterisk"></span> 
-		are required.  All others can be left blank if you wish.
+		<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
+		<?xjs write(_rpl.help_text_required); ?>
 	</div>
 
 	<form id="form-register" data-toggle="validator">
 
 		<div class="form-group">
-			<label for="alias">Username</label> <span title="Required" class="glyphicon glyphicon-asterisk"></span> 
-			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_ALIAS); ?>" class="form-control" id="alias" name="alias" placeholder="Username" required>
+			<label for="alias"><?xjs write(_rpl.input_alias); ?></label> 
+			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_ALIAS); ?>" class="form-control" id="alias" name="alias" placeholder="<?xjs write(_rpl.input_alias); ?>" required>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_ALIAS); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_ALIAS)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="password1" class="control-label">Password</label> 
-			<span title="Required" class="glyphicon glyphicon-asterisk"></span> 
-			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="Password" required>
+			<label for="password1" class="control-label">
+				<?xjs write(_rpl.input_password); ?>
+			</label> 
+			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
+			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="<?xjs write(_rpl.input_password); ?>" required>
 			<span class="help-block">
-				Minimum of <?xjs write(settings.minimum_password_length); ?>, 
-				maximum of <?xjs write(LEN_PASS); ?> characters
+				<?xjs write(format(_rpl.help_text_minimum_characters, settings.minimum_password_length)); ?>,
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_PASS)); ?>
 			</span>
 		</div>
 			<div class="form-group">
-			<label for="password2" class="control-label">Confirm Password</label> 
-			<span title="Required" class="glyphicon glyphicon-asterisk"></span> 
-			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="Confirm Password" data-match="#password1" required>
+			<label for="password2" class="control-label">
+				<?xjs write(_rpl.input_password_confirm); ?>
+			</label> 
+			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
+			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="<?xjs write(_rpl.input_password_confirm); ?>" data-match="#password1" required>
 		</div>
 
 		<div class="form-group">
-			<label for="netmail">Email address</label> 
+			<label for="netmail"><?xjs write(_rpl.input_email); ?></label> 
 			<?xjs iconNotRequired(UQ_NONETMAIL); ?>
-			<input type="email" data-minlength="6" maxlength="<?xjs write(LEN_NETMAIL); ?>"class="form-control" id="netmail" name="netmail" placeholder="pat@m.f"<?xjs write(notRequired(UQ_NONETMAIL)); ?>>
+			<input type="email" data-minlength="6" maxlength="<?xjs write(LEN_NETMAIL); ?>"class="form-control" id="netmail" name="netmail" placeholder="<?xjs write(_rpl.placeholder_netmail); ?>"<?xjs write(notRequired(UQ_NONETMAIL)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_NETMAIL); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_NETMAIL)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="realname">Real Name</label> 
+			<label for="realname"><?xjs write(_rpl.input_name); ?></label> 
 			<?xjs iconRequired(UQ_REALNAME); ?>
-			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_NAME); ?>" class="form-control" id="realname" name="realname" placeholder="Pat Androgyne"<?xjs write(required(UQ_REALNAME)); ?>>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_NAME); ?>" class="form-control" id="realname" name="realname" placeholder="<?xjs write(_rpl.placeholder_name); ?>"<?xjs write(required(UQ_REALNAME)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_NAME); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_NAME)); ?>
 			</span>
 		</div>
 
-<!--	Not sure which User property this would be
-		<div class="form-group">
-			<label for="company">Organization</label> <?xjs //iconRequired(UQ_COMPANY); ?>
-			<input type="text" class="form-control" name="company" placeholder="Company name"<?xjs //write(required(UQ_COMPANY)); ?>>
-		</div> -->
-
 		<div class="form-group">
-			<label for="address">Street address</label> 
+			<label for="address">
+				<?xjs write(_rpl.input_street_address); ?>
+			</label> 
 			<?xjs iconRequired(UQ_ADDRESS); ?>
-			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="address" name="address" placeholder="123 Any Street"<?xjs write(required(UQ_ADDRESS)); ?>>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="address" name="address" placeholder="<?xjs write(_rpl.placeholder_street_address); ?>"<?xjs write(required(UQ_ADDRESS)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_ADDRESS); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_ADDRESS)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="zipcode">Postal Code</label> 
+			<label for="zipcode"><?xjs write(_rpl.input_zipcode); ?></label> 
 			<?xjs iconRequired(UQ_ADDRESS); ?>
-			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="zipcode" name="zipcode" placeholder="10101"<?xjs write(required(UQ_ADDRESS)); ?>>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="zipcode" name="zipcode" placeholder="<?xjs write(_rpl.placeholder_zipcode); ?>"<?xjs write(required(UQ_ADDRESS)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_ADDRESS); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_ADDRESS)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="location">Location</label> 
+			<label for="location"><?xjs write(_rpl.input_location); ?></label> 
 			<?xjs iconRequired(UQ_LOCATION); ?>
-			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_LOCATION); ?>" class="form-control" id="location" name="location" placeholder="City, State"<?xjs write(required(UQ_LOCATION)); ?>>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_LOCATION); ?>" class="form-control" id="location" name="location" placeholder="<?xjs write(_rpl.placeholder_location); ?>"<?xjs write(required(UQ_LOCATION)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_LOCATION); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_LOCATION)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="phone">Telephone number</label> 
+			<label for="phone"><?xjs write(_rpl.input_phone); ?></label> 
 			<?xjs iconRequired(UQ_PHONE); ?>
-			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_PHONE); ?>" class="form-control" id="phone" name="phone" placeholder="800-555-5555"<?xjs write(required(UQ_PHONE)); ?>>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_PHONE); ?>" class="form-control" id="phone" name="phone" placeholder="<?xjs write(_rpl.placeholder_phone); ?>"<?xjs write(required(UQ_PHONE)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_PHONE); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_PHONE)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="birth">Birthdate</label> <?xjs iconRequired(UQ_BIRTH); ?>
+			<label for="birth"><?xjs write(_rpl.input_birthdate); ?></label> 
+			<?xjs iconRequired(UQ_BIRTH); ?>
 			<input type="text" data-minlength="<?xjs write(LEN_BIRTH); ?>" maxlength="<?xjs write(LEN_BIRTH); ?>" class="form-control" id="birth" name="birth" placeholder="<?xjs write(system.settings&SYS_EURODATE ? 'DD/MM/YY' : 'MM/DD/YY'); ?>" <?xjs write(required(UQ_BIRTH)); ?>>
 			<span class="help-block">
-				Maximum of <?xjs write(LEN_BIRTH); ?> characters
+				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_BIRTH)); ?>
 			</span>
 		</div>
 
 		<div class="form-inline">
-			<?xjs iconRequired(UQ_SEX); ?> <label>Gender</label>&nbsp;
+			<?xjs iconRequired(UQ_SEX); ?> 
+			<label><?xjs write(_rpl.input_gender); ?></label>&nbsp;
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-withheld" name="gender" value="X" checked> Withheld 
+					<input type="radio" id="gender-withheld" name="gender" value="X" checked> 
+					<?xjs write(_rpl.input_gender_withheld); ?> 
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-male" name="gender" value="M"> Male 
+					<input type="radio" id="gender-male" name="gender" value="M"> 
+					<?xjs write(_rpl.input_gender_male); ?>
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-female" name="gender" value="F"> Female
+					<input type="radio" id="gender-female" name="gender" value="F"> 
+					<?xjs write(_rpl.input_gender_female); ?>
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-other" name="gender" value="O"> Other 
+					<input type="radio" id="gender-other" name="gender" value="O"> 
+					<?xjs write(_rpl.input_gender_other); ?>
 				</label>
 			</div>
 		</div>
@@ -174,7 +180,9 @@
 
 		<?xjs if(system.newuser_password !== '') { ?>
 		<div class="form-group">
-			<label for="newuser-password">Registration password</label>
+			<label for="newuser-password">
+				<?xjs write(_rpl.input_registration_password); ?>
+			</label>
 			<input type="password" id="newuser-password" name="newuser-password" data-minlength="1" maxlength="8">
 		</div>
 		<?xjs } ?>
@@ -185,7 +193,7 @@
 
 		<div class="pull-right">
 			<button class="btn btn-primary" type="submit">
-				Register
+				<?xjs write(_rpl.button_register); ?>
 			</button>
 		</div>
 
@@ -212,7 +220,7 @@
 		 		} else {
 		 			$('#errorbox').attr('hidden', true);
 		 			$('#form-register-container').html(
-		 				'Your account has been created.'
+		 				'<?xjs write(_rpl.message_account_created); ?>'
 		 			);
 		 		}
 		 	}
-- 
GitLab


From a56287eb2f8fbfd9f116255fbf77bbfa9f04fe8a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 15:51:33 -0500
Subject: [PATCH 258/752] Prefix section names with page_, sidebar_, api_.

---
 web/lib/language/english.ini         | 18 +++++++++++-------
 web/root/pages/000-home.xjs          |  4 ++--
 web/root/pages/000-mail.xjs          |  2 +-
 web/root/pages/000-register.xjs      |  2 +-
 web/root/pages/002-files.xjs         |  2 +-
 web/root/sidebar/001-nodelist.xjs    |  2 +-
 web/root/sidebar/003-systemStats.xjs |  2 +-
 7 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
index 60d9ab8ad7..ebb885f9f8 100644
--- a/web/lib/language/english.ini
+++ b/web/lib/language/english.ini
@@ -11,10 +11,10 @@ menu_item_logout = Log out
 menu_item_mail = Mail
 menu_item_register = Register
 
-[home]
+[page_home]
 button_ftelnet = Connect via Telnet
 
-[mail]
+[page_mail]
 button_post_new = Post a new message
 button_select_all = Select all messages
 button_delete_selected = Delete selected messages
@@ -25,7 +25,7 @@ label_message_subject = Subject
 label_tab_inbox = Inbox
 label_tab_sent = Sent
 
-[register]
+[page_register]
 title = Register
 button_register = Register
 label_field_required = Required
@@ -57,20 +57,20 @@ placeholder_zipcode = H0H0H0
 placeholder_location = City, State
 placeholder_phone = 800-555-5555
 
-[files]
+[page_files]
 title = Files
 stat_suffix_file = file
 stat_suffix_files = files
 stat_suffix_directory = directory
 stat_suffix_directories = directories
 
-[node_list]
+[sidebar_node_list]
 label_title = Who's Online
 label_node_column = Node
 label_send_telegram = Send a telegram
 label_status_column = Status
 
-[system_info]
+[sidebar_system_info]
 label_title = System Info
 label_sysop = Sysop:
 label_location = Location:
@@ -85,4 +85,8 @@ label_files_downloaded_today = D/L today:
 label_messages_total = Messages:
 label_messages_posted_today = Posted today:
 stat_suffix_files = files
-stat_suffix_bytes = bytes
\ No newline at end of file
+stat_suffix_bytes = bytes
+
+[api_system]
+nodelist_action_prefix = viewing
+telegram_header_format = Telegram from %s via WWW on %s
\ No newline at end of file
diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 687ed896cb..23e8e74914 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,8 +1,8 @@
 <!--Home-->
 <?xjs
-	if (typeof argv[0] != 'boolean' || !argv[0]) exit(); // I don't remember what this is for
+	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
-	var _hpl = getLanguage(settings.language_file || 'english.ini', 'home'); ?>
+	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home'); ?>
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
diff --git a/web/root/pages/000-mail.xjs b/web/root/pages/000-mail.xjs
index 9341d37f56..1e3710cae8 100644
--- a/web/root/pages/000-mail.xjs
+++ b/web/root/pages/000-mail.xjs
@@ -9,7 +9,7 @@ load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'forum.js');
 
-var _mpl = getLanguage(settings.language_file || 'english.ini', 'mail');
+var _mpl = getLanguage(settings.language_file || 'english.ini', 'page_mail');
 
 function sent() {
 	if (typeof http_request.query.sent === 'undefined') return false;
diff --git a/web/root/pages/000-register.xjs b/web/root/pages/000-register.xjs
index 1e166135cb..b719af918b 100644
--- a/web/root/pages/000-register.xjs
+++ b/web/root/pages/000-register.xjs
@@ -12,7 +12,7 @@
 
 	load('sbbsdefs.js');
 
-	var _rpl = getLanguage(settings.language_file || 'english.ini', 'register');
+	var _rpl = getLanguage(settings.language_file || 'english.ini', 'page_register');
 
 	function required(mask) {
 		return ((system.new_user_questions&mask) ? (' ' + _rpl.stat_suffix_field_required) : '');
diff --git a/web/root/pages/002-files.xjs b/web/root/pages/002-files.xjs
index f8a37740c1..d2dd4fa35d 100644
--- a/web/root/pages/002-files.xjs
+++ b/web/root/pages/002-files.xjs
@@ -5,7 +5,7 @@
 	load(system.exec_dir + '../web/lib/init.js');
 	load(settings.web_lib + 'files.js');
 
-	var _fpl = getLanguage(settings.language_file || 'english.ini', 'files');
+	var _fpl = getLanguage(settings.language_file || 'english.ini', 'page_files');
 
 	if (typeof http_request.query.dir !== 'undefined' &&
 		typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
diff --git a/web/root/sidebar/001-nodelist.xjs b/web/root/sidebar/001-nodelist.xjs
index c65f907b71..fe5fb571e0 100644
--- a/web/root/sidebar/001-nodelist.xjs
+++ b/web/root/sidebar/001-nodelist.xjs
@@ -1,4 +1,4 @@
-<?xjs var _nll = getLanguage(settings.language_file || 'english.ini', 'node_list'); ?>
+<?xjs var _nll = getLanguage(settings.language_file || 'english.ini', 'sidebar_node_list'); ?>
 
 <div id="sbbs-nodelist"></div>
 <script type="text/javascript">
diff --git a/web/root/sidebar/003-systemStats.xjs b/web/root/sidebar/003-systemStats.xjs
index 803599779e..59dfec2e28 100644
--- a/web/root/sidebar/003-systemStats.xjs
+++ b/web/root/sidebar/003-systemStats.xjs
@@ -1,4 +1,4 @@
-<?xjs var _sill = getLanguage(settings.language_file || 'english.ini', 'system_info'); ?>
+<?xjs var _sill = getLanguage(settings.language_file || 'english.ini', 'sidebar_system_info'); ?>
 
 <h4>System Info</h4>
 <table class="table table-condensed table-responsive table-striped">
-- 
GitLab


From 52b1c737e6cbac7091052890eebd90c415501835 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 15:52:05 -0500
Subject: [PATCH 259/752] Use strings from language file when returning
 nodelist or sending telegrams.

---
 web/root/api/system.ssjs | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 3e80d1f498..48f0e30046 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -2,6 +2,9 @@ load('sbbsdefs.js');
 load('nodedefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'language.js');
+
+var _language = getLanguage(settings.language_file || 'english.ini', 'api_system');
 
 var reply = {};
 
@@ -39,7 +42,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				if (webAction === null) continue;
 				reply.push(
 					{	status : '',
-						action : 'viewing ' + webAction,
+						action : _language.nodelist_action_prefix + ' ' + webAction,
 						user : usr.alias
 					}
 				);
@@ -63,9 +66,10 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			if (un < 1) break;
 			system.put_telegram(
 				un,
-				'Telegram from ' +
-				user.alias + ' via WWW on ' + system.timestr() + '\r\n' +
-				http_request.query.telegram[0] + '\r\n'
+				format(
+					_language.telegram_header_format,
+					user.alias, (new Date()).toLocaleString()
+				) + '\r\n' + http_request.query.telegram[0] + '\r\n'
 			);
 			break;
 
@@ -94,4 +98,4 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 reply = JSON.stringify(reply);
 http_reply.header['Content-Type'] = 'application/json';
 http_reply.header['Content-Length'] = reply.length;
-write(reply);
+write(reply);
\ No newline at end of file
-- 
GitLab


From f220512e1a44c05b65fe1772e0543c057e845ca6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 16:23:02 -0500
Subject: [PATCH 260/752] Removed hard-coded enlish lanuage strings that make
 my legg hurt.  Get error and log messages from the language file instead.

---
 web/lib/language/english.ini | 20 ++++++++++++++++-
 web/root/api/register.ssjs   | 42 +++++++++++++++++++-----------------
 2 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
index ebb885f9f8..41b7543108 100644
--- a/web/lib/language/english.ini
+++ b/web/lib/language/english.ini
@@ -89,4 +89,22 @@ stat_suffix_bytes = bytes
 
 [api_system]
 nodelist_action_prefix = viewing
-telegram_header_format = Telegram from %s via WWW on %s
\ No newline at end of file
+telegram_header_format = Telegram from %s via WWW on %s
+
+[api_register]
+log_success = User #%d registered via HTTP
+log_bot_attempt = Hidden registration form field filled. Likely a bot. Cancelling registration.
+error_failed = Failed to create user record.
+error_bad_syspass = Incorrect registration password.
+error_invalid_alias = Valid username is required.
+error_alias_taken = Username already taken.
+error_password_mismatch = Password and confirmation are required, and must match.
+error_password_length = Password must be %d to %d characters in length.
+error_email_required = Valid email address is required.
+error_invalid_email = Invalid email address
+error_invalid_name = Valid real name is required.
+error_invalid_location = Valid location is required.
+error_invalid_street_address = Valid street address and postal code are required
+error_invalid_phone = Valid phone number is required.
+error_invalid_gender = Sex is required. Heh heh heh.
+error_invalid_birthdate = Valid birthdate is required.
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 6fc60d1016..a9dd602cca 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,10 +1,13 @@
 load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + '/auth.js');
+load(settings.web_lib + '/language.js');
 
 if (user.alias !== settings.guest) exit();
 if (!settings.user_registration) exit();
 
+var _rl = getLanguage(settings.language_file || 'english.ini', 'api_register');
+
 var MIN_ALIAS = 1,
 	MIN_REALNAME = 3,
 	MIN_NETMAIL = 6,
@@ -67,10 +70,10 @@ function paramLength(param) {
 function newUser() {
 	var usr = system.new_user(prepUser.alias);
 	if (typeof usr === 'number') {
-		reply.errors.push('Failed to create user record.');
+		reply.errors.push(_rl.error_failed);
 		return;
 	}
-	log(LOG_INFO, 'User #' + usr.number + ' registered via HTTP.');
+	log(LOG_INFO, format(_rl.log_success, usr.number));
 	usr.security.password = prepUser.password;
 	for (var property in prepUser) {
 		if (property === 'alias' || property === 'password') continue;
@@ -87,10 +90,7 @@ if ((	paramExists('send-me-free-stuff') &&
 		http_request.query['subscribe-to-newsletter'][0] !== ''
 	)
 ) {
-	log(LOG_WARNING,
-		'Hidden registration form input element filled.  ' +
-		'Likely a bot.  Cancelling user registration.'
-	);
+	log(LOG_WARNING, _rl.log_bot_attempt);
 	exit();
 }
 
@@ -99,7 +99,7 @@ if (system.newuser_password !== '' &&
 		http_request.query['newuser-password'][0] != system.newuser_password
 	)
 ) {
-	reply.errors.push('Incorrect registration password.');
+	reply.errors.push(_rl.error_bad_syspass);
 }
 
 // More could be done to respect certain newuser question toggles
@@ -109,9 +109,9 @@ if (!paramExists('alias') ||
 	paramLength('alias') < MIN_ALIAS ||
 	paramLength('alias') > LEN_ALIAS
 ) {
-	reply.errors.push('Valid username is required.');
+	reply.errors.push(_rl.error_invalid_alias);
 } else if (system.matchuser(http_request.query.alias[0]) > 0) {
-	reply.errors.push('Username already taken.');
+	reply.errors.push(_rl.error_alias_taken);
 } else {
 	prepUser.alias = cleanParam('alias');
 	prepUser.handle = cleanParam('alias');
@@ -120,27 +120,29 @@ if (!paramExists('alias') ||
 if ((!paramExists('password1') || !paramExists('password2')) ||
 	http_request.query.password1[0] !== http_request.query.password2[0]
 ) {
-	reply.errors.push('Password & confirmation are required, and must match.');
+	reply.errors.push(_rl.error_password_mismatch);
 } else if (
 	paramLength('password1') < settings.minimum_password_length ||
 	paramLength('password1') > LEN_PASS
 ) {
 	reply.errors.push(
-		'Password must be between ' +
-		settings.minimum_password_length + ' and ' + LEN_PASS + ' in length.'
+		format(
+			_rl.error_password_length,
+			settings.minimum_password_length, LEN_PASS
+		)
 	);
 } else {
 	prepUser.password = cleanParam('password1');
 }
 
 if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
-	reply.errors.push('Email address is required.');
+	reply.errors.push(_rl.error_email_required);
 } else if (
 	(	paramLength('netmail') < MIN_NETMAIL ||
 		paramLength('netmail') > LEN_NETMAIL
 	) && !required(UQ_NONETMAIL)
 ) {
-	reply.errors.push('Invalid email address.');
+	reply.errors.push(_rl.error_invalid_email);
 } else {
 	prepUser.netmail = cleanParam('netmail');
 }
@@ -151,7 +153,7 @@ if (required(UQ_REALNAME) &&
 		paramLength('realname') > LEN_NAME
 	)
 ) {
-	reply.errors.push('Valid real name is required.');
+	reply.errors.push(_rl.error_invalid_name);
 } else {
 	prepUser.name = cleanParam('realname');
 }
@@ -162,7 +164,7 @@ if (required(UQ_LOCATION) &&
 		paramLength('location') > LEN_LOCATION
 	)
 ) {
-	reply.errors.push('Valid location is required.');
+	reply.errors.push(_rl.error_invalid_location);
 } else {
 	prepUser.location = cleanParam('location');
 }
@@ -176,7 +178,7 @@ if (required(UQ_ADDRESS) &&
 		paramLength('zipcode') > LEN_ADDRESS
 	)
 ) {
-	reply.errors.push('Valid street address and postal code are required.');
+	reply.errors.push(_rl.error_invalid_street_address);
 } else {
 	prepUser.address = cleanParam('address');
 	prepUser.zipcode = cleanParam('zipcode');
@@ -188,7 +190,7 @@ if (required(UQ_PHONE) &&
 		paramLength('phone') > LEN_PHONE
 	)
 ) {
-	reply.errors.push('Valid phone number is required.');
+	reply.errors.push(_rl.error_invalid_phone);
 } else {
 	prepUser.phone = cleanParam('phone');
 }
@@ -199,7 +201,7 @@ if (required(UQ_SEX) &&
 		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
 	)
 ) {
-	reply.errors.push('Sex is required. Heh heh.');
+	reply.errors.push(_rl.error_invalid_gender);
 } else {
 	prepUser.gender = http_request.query.gender[0];
 }
@@ -210,7 +212,7 @@ if (paramExists('birth') &&
 	// Should really test for valid date (and date format per system config)
 	prepUser.birthdate = cleanParam('birth');
 } else if (required(UQ_BIRTH)) {
-	reply.errors.push('Birthdate is required.');
+	reply.errors.push(_rl.error_invalid_birthdate);
 }
 
 if (reply.errors.length < 1) newUser();
-- 
GitLab


From 14e0665453685c65a45d112bc658dabdd6335d09 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 16:40:12 -0500
Subject: [PATCH 261/752] Removed extra XJS closing doodle.

---
 web/root/pages/000-home.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 23e8e74914..431b1a637f 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -2,7 +2,7 @@
 <?xjs
 	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
-	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home'); ?>
+	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
-- 
GitLab


From a47c4e76a126ac3e4abfce9ddd47cc81ef37fa74 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 10 Jan 2017 17:52:37 -0500
Subject: [PATCH 262/752] Remove code tabbing from inside of <pre> tags. derp.

---
 web/root/pages/002-files.xjs | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/web/root/pages/002-files.xjs b/web/root/pages/002-files.xjs
index d2dd4fa35d..21c7862f64 100644
--- a/web/root/pages/002-files.xjs
+++ b/web/root/pages/002-files.xjs
@@ -36,13 +36,9 @@
 			<?xjs if (typeof file.extdesc === 'undefined') { ?>
 				<p><?xjs write(file.desc); ?></p>
 			<?xjs } else if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) { ?>
-				<pre class="ansi">
-					<?xjs write(html_encode(file.extdesc, true, false, true, true)); ?>
-				</pre>
+				<pre class="ansi"><?xjs write(html_encode(file.extdesc, true, false, true, true)); ?></pre>
 			<?xjs } else { ?>
-				<pre class="list">
-					<?xjs write(file.extdesc.replace(/[^\r,\n\x20-\x7E]/g, '')); ?>
-				</pre>
+				<pre class="list"><?xjs write(file.extdesc.replace(/[^\r,\n\x20-\x7E]/g, '')); ?></pre>
 			<?xjs } ?>
 		</a>
 	<?xjs } ?>
-- 
GitLab


From 58f528d5b225ce3fabb83215c46be9d691dc5283 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 11 Jan 2017 17:59:34 -0500
Subject: [PATCH 263/752] Include attr:MSG_POLL in header when using
 MsgBase.add_poll; not sure if it made the difference, but a problem observed
 earlier has gone away.

---
 web/lib/forum.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 231f32cbc4..f3a4b8da81 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -525,6 +525,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
     }
 
     var header = {
+        attr : MSG_POLL,
         subject : subject.substr(0, LEN_TITLE),
         from : msg_area.sub[sub].settings&SUB_NAME ? user.name : user.alias,
         from_ext : user.number,
-- 
GitLab


From 9043ad5143ae3640b9c592dc250ebd8984619102 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 12 Jan 2017 20:32:35 -0500
Subject: [PATCH 264/752] Fixed 'undefined' sidebar toggle text in mobile view.

---
 web/root/index.xjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 26993ff1f3..e11f360917 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -129,7 +129,7 @@
 				<div class="navbar-header">
 					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
 						<span class="sr-only">
-							<?xjs write(_language.sidebar_label); ?>
+							<?xjs write(_language.label_sidebar); ?>
 						</span>
 						<span class="icon-bar"></span>
 						<span class="icon-bar"></span>
@@ -205,7 +205,7 @@
 					<p class="pull-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?> visible-xs">
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
 							<span class="glyphicon glyphicon-tasks"></span>
-							<?xjs write(_language.sidebar_label); ?>
+							<?xjs write(_language.label_sidebar); ?>
 						</button>
 					</p>
 					<?xjs writePage(); ?>
-- 
GitLab


From cb41b2d4f41ec89afc1d81025aed8bc219233005 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 14 Jan 2017 10:36:55 -0500
Subject: [PATCH 265/752] Added CSS clear:both to #fTelnetContainer to stop it
 from getting in front of the sidebar toggle.

---
 web/root/pages/000-home.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 431b1a637f..5ef05f35cf 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -6,7 +6,7 @@
 ?>
 
 <script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
-<div id="fTelnetContainer" style="margin-bottom:1em;"></div>
+<div id="fTelnetContainer" style="margin-bottom:1em;clear:both;"></div>
 <div class="row">
 	<div class="center-block" style="width:200px;margin-bottom:1em;">
 		<button id="ftelnet-connect" class="btn btn-primary">
-- 
GitLab


From b69d47c92043c8b15e1636f9abeaeb652942266e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 14 Jan 2017 20:04:56 -0500
Subject: [PATCH 266/752] Additional checks before updating scan pointer; don't
 bother if the user is guest, or if the 'newest' message number is invalid.

---
 web/root/pages/001-forum.ssjs | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index 8db8ed2c91..c5f5a7118b 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -376,17 +376,22 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		writeln(strings.thread_view.container.open);
 		keys.forEach(function (key, index) { writeMessage(thread, keys, key, index); });
 		writeln(strings.thread_view.container.close);
-		msgBase.close();
 		if (keys.length > 1 && firstUnread !== '') {
 			writeln(strings.script.open);
 			writeln(format(strings.thread_view.set_unread, firstUnread));
 			writeln(strings.script.close);
 		}
 		// Update scan pointer
-		if (thread.messages[thread.__last].number > msg_area.sub[http_request.query.sub[0]].scan_ptr) {
+		if (user.alias !== settings.guest &&
+			!isNaN(thread.messages[thread.__last].number) &&
+			thread.messages[thread.__last].number < 0xFFFFFFFF &&
+			thread.messages[thread.__last].number > msg_area.sub[http_request.query.sub[0]].scan_ptr &&
+			thread.messages[thread.__last].number <= msgBase.last_msg
+		) {
 			msg_area.sub[http_request.query.sub[0]].scan_ptr =
 			thread.messages[thread.__last].number;
 		}
+		msgBase.close();
 
 		writeln(strings.script.open);
 		if (settings.keyboard_navigation) writeln(strings.script.thread_navigation);
-- 
GitLab


From c6c76a329aff4327b2aea5e2dd5b988898302e1b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 14 Jan 2017 20:09:43 -0500
Subject: [PATCH 267/752] Append commit URL to each commit block in the
 message.  (Requested by Ragnarok.)

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 0bbf5f4ac0..168a8b693b 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -68,7 +68,7 @@ var body = payload.commits.map(
 		if (e.added.length > 0) ret.push('Added: ' + e.added.join(', '));
 		if (e.removed.length > 0) ret.push('Removed: ' + e.removed.join(', '));
 		if (e.modified.length > 0) ret.push('Modified: ' + e.modified.join(', '));
-		ret.push('', 'Message:', e.message, '');
+		ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
 		return ret.join('\r\n');
 	}
 );
-- 
GitLab


From c8368b44146e98c05192eaa7811854c307281370 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 23 Feb 2017 08:29:39 -0500
Subject: [PATCH 268/752] Fixed thread date-sorting bug. Stop at
 msgBase.last_msg in getSomeMessageHeaders (Noisome).

---
 web/lib/forum.js | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index f3a4b8da81..2c27a70e78 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -693,7 +693,7 @@ function submitPollAnswers(sub, number, answers) {
 function linkify(body) {
     urlRE = /(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
     body = body.replace(
-        urlRE, 
+        urlRE,
         function (str) {
             var ret=''
             var p=0;
@@ -730,7 +730,7 @@ function quotify(body) {
             var broken = false;
 
             line = lines[l];
-            
+
             // If the new length is smaller than the old one, close the extras
             for (p = new_prefixes.length; p < prefixes.length; p++) {
                 if (quote_depth < 1) continue;
@@ -881,7 +881,9 @@ function getMessageThreads(sub, max) {
 
     function addToThread(thread_id, header, subject) {
         if (typeof subject !== 'undefined') subjects[subject] = thread_id;
-        threads.thread[thread_id].newest = header.when_written_time;
+        if (header.when_written_time > threads.thread[thread_id].newest) {
+            threads.thread[thread_id].newest = header.when_written_time;
+        }
         threads.thread[thread_id].messages[header.number] = {
             attr : header.attr,
             auxattr : header.auxattr,
@@ -931,7 +933,7 @@ function getMessageThreads(sub, max) {
         if (start < msgBase.first_msg) start = msgBase.first_msg;
         var headers = {};
         var c = 0;
-        for (var m = start; m < msgBase.last_msg; m++) {
+        for (var m = start; m <= msgBase.last_msg; m++) {
             var header = msgBase.get_msg_header(m);
             if (header === null || header.attr&MSG_DELETE) continue;
             headers[header.number] = header;
@@ -1066,4 +1068,4 @@ function getMessageThreads(sub, max) {
 
     return threads;
 
-}
\ No newline at end of file
+}
-- 
GitLab


From 1419a92ae2bb1bf50fd2a502a1307b151f2c32d6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 30 Jun 2017 10:41:23 -0400
Subject: [PATCH 269/752] Use Path header from client if it matches a hostname
 and port in modopts.ini [web] allowed_ftelnet_targets list.

---
 mods/websocket-telnet-service.js | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index 7dc6dae15e..b3c2d6c47e 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -1,5 +1,6 @@
 load('sbbsdefs.js');
 load('websocket-proxy.js');
+load('modopts.js');
 
 function log_err(msg) {
 	log(LOG_DEBUG, msg);
@@ -235,8 +236,21 @@ try {
     }
 
     var wss = new WebSocketProxy(client);
-    log(LOG_DEBUG, 'WSTS Connecting to ' + telnet_addr + ':' + ini.TelnetPort);
-    var telnet = new TelnetClient(telnet_addr, ini.TelnetPort);
+	var wsspath = wss.headers.Path.split('/');
+	if (wsspath.length < 3 || isNaN(parseInt(wsspath[2]))) {
+		var telnet = new TelnetClient(telnet_addr, ini.TelnetPort);
+	} else {
+		var _settings = get_mod_options('web');
+		if (typeof _settings.allowed_ftelnet_targets !== 'string') {
+			throw 'Client supplied Path but no allowed_ftelnet_targets supplied in modopts.ini [web] section.';
+		}
+		var targets = _settings.allowed_ftelnet_targets.split(',');
+		if (!targets.some(function (e) { var target = e.split(':'); return target[0] === wsspath[1] && target[1] === wsspath[2]; })) {
+			throw 'Client supplied Path is not in allowed_ftelnet_targets list.';
+		}
+		log('Using client-supplied target ' + wsspath[1] + ':' + wsspath[2]);
+		var telnet = new TelnetClient(wsspath[1], parseInt(wsspath[2]));
+	}
 
 	while (client.socket.is_connected && telnet.connected) {
 
@@ -265,4 +279,4 @@ try {
 
 	client.socket.close();
 
-}
\ No newline at end of file
+}
-- 
GitLab


From 7a38af83f5392d85c4b0ac3bf31263862d59fb92 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 30 Jun 2017 10:45:03 -0400
Subject: [PATCH 270/752] Attempt to restore section headings.

---
 README.md | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md
index 520f3a2dd7..6818f36d1b 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,17 @@
 # synchronet-web-v4
 A web interface for Synchronet BBS
 
-###Disclaimer
+### Disclaimer
 
 - Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
 
-###Requirements
+### Requirements
 
 - This web interface has been tested with Synchronet BBS 3.17a.  It will probably work with later versions.
 - In addition to updating your Synchronet build, be sure to update your the javascript modules in your *exec* and *exec/load* directories as well.  You can get them from [the Synchronet CVS repository](http://cvs.synchro.net/) or in the [sbbs_run.zip archive](http://vert.synchro.net/Synchronet/sbbs_run.zip).
 	- Do *not* simply unzip sbbs_run.zip overtop your existing sbbs directory structure.  This may overwrite much of your local configuration.  Extract the archive elsewhere and copy things over piecemeal as needed.
 
-###Quick start
+### Quick start
 
 - Back up your Synchronet installation (particularly 'ctrl' and 'web')
 - Shut down your BBS
@@ -101,12 +101,12 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 
 - Start your BBS back up again
 
-###Configuration
+### Configuration
 
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
 - Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
 
-####Additional / advanced settings
+#### Additional / advanced settings
 
 The following *optional* settings can be added to the [web] section of your *ctrl/modopts.ini* file:
 
@@ -124,13 +124,13 @@ The following *optional* settings can be added to the [web] section of your *ctr
 - Tweaking *max_messages* may improve forum performance or resolve 'Out of memory' errors, if you see any of those when browsing the forum
 - Normally, fTelnet will try to connect to the websocket proxies based on information from *ctrl/services.ini*.  In some situations (eg. when using an HTTPS reverse proxy) it may be necessary to connect to another port unknown to Synchronet.
 
-###Customization
+### Customization
 
 This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
 
 - You can place your own CSS overrides in *web/root/css/custom.css*.  Create this file and use it if you want to use fonts, colours, etc. other than the defaults.  It is not recommended that you modify any of the existing stylesheets.
 
-####Sidebar Modules
+#### Sidebar Modules
 
 Sidebar modules are the widgets displayed in the narrow column running down the right (or left) side of the page.  A sidebar module can be an SSJS, XJS, HTML, or TXT file.
 
@@ -139,7 +139,7 @@ Sidebar modules are the widgets displayed in the narrow column running down the
 - TXT sidebar modules are displayed inside of \<pre\> tags to preserve fixed-width formatting.
 - Support for additional file formats can be added if necessary, but by using HTML and Javascript you should be able to display whatever you like.  (If you want to put an image in the sidebar, a simple HTML file containing an \<img\> tag will do the job, for example.)
 
-####Pages
+#### Pages
 
 Like sidebar modules, pages can be HTML, XJS, SSJS, or TXT files.
 
@@ -192,7 +192,7 @@ You can add drop-down menus to the navigation bar by adding subdirectories to th
 - Subdirectories with names beginning with *.* will be ignored.
 - Each subdirectory can contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file for access control.
 
-###Uninstall
+### Uninstall
 
 To stop using this web interface, you can just revert to your previous *web* directory at any time.
 
@@ -200,21 +200,21 @@ To stop using this web interface, you can just revert to your previous *web* dir
 - Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
 - Undo any changes you made to your firewall & router during the *Quick Start*
 
-###Support
+### Support
 
-####Via GitHub
+#### Via GitHub
 
 - Please browse the existing [issues](https://github.com/echicken/synchronet-web-v4/issues) for this project, including those marked as closed.  You may find that your question has already been asked (and hopefully answered).
 - Open a new [issue](https://github.com/echicken/synchronet-web-v4/issues) here on GitHub
 
-####On DOVE-Net
+#### On DOVE-Net
 
 - Post a message to *echicken* in [Synchronet Discussion](https://bbs.electronicchicken.com/?page=001-forum.ssjs&sub=sync).  I read this sub regularly and will respond to you there.
 
-####On IRC
+#### On IRC
 
 - You can find me in #synchronet on irc.synchro.net.  I may be AFK, but ask your question and idle for a while; I'll respond eventually.
 
-####Email
+#### Email
 
 - Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
-- 
GitLab


From 612ce1a285ae64fd51a9e53f7a68c612571c46ba Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 30 Jun 2017 11:02:35 -0400
Subject: [PATCH 271/752] Document the allowed_ftelnet_targets key in
 modopts.ini [web]; provide example for proxying to some other BBS.

---
 README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/README.md b/README.md
index 6818f36d1b..a962035e6b 100644
--- a/README.md
+++ b/README.md
@@ -118,11 +118,13 @@ The following *optional* settings can be added to the [web] section of your *ctr
 	; Connect to the websocket proxies on ports other than specified in ctrl/services.ini
 	websocket_telnet_port = 1124
 	websocket_rlogin_port = 1514
+	allowed_ftelnet_targets = some.other.bbs:23,yet.another.bbs:2323
 ```
 
 - Setting *forum_extended_ascii* to *false* may resolve problems with displaying UTF-8 encoded characters; character codes > 127 will not be assumed to be extended-ASCII references to CP437 characters.
 - Tweaking *max_messages* may improve forum performance or resolve 'Out of memory' errors, if you see any of those when browsing the forum
 - Normally, fTelnet will try to connect to the websocket proxies based on information from *ctrl/services.ini*.  In some situations (eg. when using an HTTPS reverse proxy) it may be necessary to connect to another port unknown to Synchronet.
+- The *allowed_ftelnet_targets* setting is only necessary if you will be adding additional pages through which users can connect to external systems via fTelnet (see below).  This is a comma-separated list of host:port entries.
 
 ### Customization
 
@@ -192,6 +194,52 @@ You can add drop-down menus to the navigation bar by adding subdirectories to th
 - Subdirectories with names beginning with *.* will be ignored.
 - Each subdirectory can contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file for access control.
 
+#### Additional fTelnet targets
+
+The fTelnet embed on the home page is configured to automatically connect to a BBS on the same system that is hosting the website.  If you run another BBS or wish to allow users of your website to connect via fTelnet to some other BBS:
+
+- Edit *ctrl/modopts.ini*
+- In the *[web]* section, add the *allowed_ftelnet_targets* key if it doesn't already exist (see *Additional/advanced settings* above)
+- Add an entry to the list for each additional host you want to allow fTelnet to connect to
+- Create a new *.xjs* file in your *pages/* directory or in a subdirectory thereof.  In this example, I've created */sbbs/web/root/pages/More/006-some-other-bbs.xjs*, and populate it with the following content:
+
+```html
+<!--Some Other BBS-->
+<?xjs
+	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
+	load(settings.web_lib + 'ftelnet.js');
+?>
+
+<script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
+<div id="fTelnetContainer" style="margin-bottom:1em;clear:both;"></div>
+<div class="row">
+	<div class="center-block" style="width:200px;margin-bottom:1em;">
+		<button id="ftelnet-connect" class="btn btn-primary">Connect via Telnet</button>
+	</div>
+</div>
+<script type="text/javascript">
+	fTelnet.Hostname = 'valhalla.synchro.net';
+	fTelnet.Port = 23;
+    fTelnet.ProxyHostname = '<?xjs write(http_request.vhost); ?>';
+    fTelnet.ProxyPort = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
+    fTelnet.ProxyPortSecure = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
+	fTelnet.ConnectionType = 'telnet';
+	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
+	fTelnet.StatusBarVisible = false;
+	fTelnet.VirtualKeyboardVisible = (
+		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
+			navigator.userAgent
+		)
+	);
+	fTelnet.Init();
+	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+</script>
+```
+
+- Edit the first line so that the title of the page (*Some Other BBS*) is to your liking
+- Edit the *fTelnet.Hostname* line so that the text within the quotes points to the system you want to target
+- Edit the *fTelnet.Port* line if the target system's telnet server is listening on some port other than *23*
+
 ### Uninstall
 
 To stop using this web interface, you can just revert to your previous *web* directory at any time.
-- 
GitLab


From 1b7687b83a5fb956a58abf07508fd8a0d89173dc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 30 Jun 2017 11:03:57 -0400
Subject: [PATCH 272/752] bleh.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a962035e6b..e924041e64 100644
--- a/README.md
+++ b/README.md
@@ -201,7 +201,7 @@ The fTelnet embed on the home page is configured to automatically connect to a B
 - Edit *ctrl/modopts.ini*
 - In the *[web]* section, add the *allowed_ftelnet_targets* key if it doesn't already exist (see *Additional/advanced settings* above)
 - Add an entry to the list for each additional host you want to allow fTelnet to connect to
-- Create a new *.xjs* file in your *pages/* directory or in a subdirectory thereof.  In this example, I've created */sbbs/web/root/pages/More/006-some-other-bbs.xjs*, and populate it with the following content:
+- Create a new *.xjs* file in your *pages/* directory or in a subdirectory thereof.  (In this example, I've created */sbbs/web/root/pages/More/006-some-other-bbs.xjs*.)  Populate it with the following content:
 
 ```html
 <!--Some Other BBS-->
-- 
GitLab


From 9aa18713b2966bf3b0ecd00f53df4114b7cc3821 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 13 Jul 2017 13:17:37 -0400
Subject: [PATCH 273/752] Work with the latest release of fTelnet.  Updating
 this without updating your local fTelnet drelease will break your shit.

---
 web/root/pages/000-home.xjs | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 5ef05f35cf..1eca4b9cb3 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -5,7 +5,8 @@
 	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
 ?>
 
-<script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
+<script src="./ftelnet/ftelnet.norip.xfer.min.js" id="fTelnetScript"></script>
+<style>.fTelnetStatusBar { display : none; }</style>
 <div id="fTelnetContainer" style="margin-bottom:1em;clear:both;"></div>
 <div class="row">
 	<div class="center-block" style="width:200px;margin-bottom:1em;">
@@ -15,16 +16,16 @@
 	</div>
 </div>
 <script type="text/javascript">
-	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;
-	fTelnet.ConnectionType = 'telnet';
-	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
-	fTelnet.StatusBarVisible = false;
-	fTelnet.VirtualKeyboardVisible = (
+	var Options = new fTelnetOptions();
+	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
+	Options.Port = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;
+	Options.ConnectionType = 'telnet';
+	Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+	Options.VirtualKeyboardVisible = (
 		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
 			navigator.userAgent
 		)
 	);
-	fTelnet.Init();
-	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+	var ftClient = new fTelnetClient('fTelnetContainer', Options);
+	$('#ftelnet-connect').click(function() { ftClient.Connect(); });
 </script>
-- 
GitLab


From 884e76781f10cbbf723b0406571740caf992bb8b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 13 Jul 2017 13:34:17 -0400
Subject: [PATCH 274/752] Work with the latest fTelnet release, probably stop
 working with earlier ones. Reload the page on fTelnet disconnect to avoid
 various problems.

---
 web/root/pages/003-games.xjs | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index 00ede7ab11..7edff0c0c8 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -1,5 +1,5 @@
 <!--Games-->
-<?xjs 
+<?xjs
 
 	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
@@ -33,7 +33,8 @@
 
 ?>
 
-<script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
+<script src="./ftelnet/ftelnet.norip.xfer.min.js" id="fTelnetScript"></script>
+<style>.fTelnetStatusBar { display : none; }</style>
 
 <a name="fTelnet"></a>
 <div id="fTelnetContainer" style="margin-bottom:1em;"></div>
@@ -51,26 +52,23 @@
 
 <script type="text/javascript">
 
-	fTelnet.Hostname = '<?xjs write(http_request.vhost); ?>';
-	fTelnet.Port = <?xjs write(settings.websocket_rlogin_port || webSocketRLogin.Port); ?>;
-	fTelnet.ConnectionType = 'telnet';
-	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
-	fTelnet.StatusBarVisible = false;
-	fTelnet.VirtualKeyboardVisible = (
+	var Options = new fTelnetOptions();
+	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
+	Options.Port = <?xjs write(settings.websocket_rlogin_port || webSocketRLogin.Port); ?>;
+	Options.ConnectionType = 'telnet';
+	Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+	Options.VirtualKeyboardVisible = (
 		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
 			navigator.userAgent
 		)
 	);
-	function ftInit() {
-		$('#fTelnetContainer').html('');
-		fTelnet.Init();
-		fTelnet.OnConnectionClose = ftInit;
-	}
-	$(document).ready(ftInit);
+	var ftClient = new fTelnetClient('fTelnetContainer', Options);
+	ftClient.OnConnectionClose = function () { window.location.reload(); };
+
 	function launchXtrn(code) {
 		$.getJSON(
 			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function(data) { fTelnet.Connect(); }
+			function(data) { ftClient.Connect(); }
 		);
 	}
 
-- 
GitLab


From 173ec505874fdbc00c43ebc041c9396cd27a9cee Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 13 Jul 2017 13:59:36 -0400
Subject: [PATCH 275/752] Updated 'Additional fTelnet targets' example with new
 fTelnet init stuff.

---
 README.md | 25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/README.md b/README.md
index e924041e64..3c96bd9aa6 100644
--- a/README.md
+++ b/README.md
@@ -210,7 +210,8 @@ The fTelnet embed on the home page is configured to automatically connect to a B
 	load(settings.web_lib + 'ftelnet.js');
 ?>
 
-<script src="./ftelnet/ftelnet.min.js" id="fTelnetScript"></script>
+<script src="./ftelnet/ftelnet.norip.xfer.min.js" id="fTelnetScript"></script>
+<style>.fTelnetStatusBar { display : none; }</style>
 <div id="fTelnetContainer" style="margin-bottom:1em;clear:both;"></div>
 <div class="row">
 	<div class="center-block" style="width:200px;margin-bottom:1em;">
@@ -218,21 +219,21 @@ The fTelnet embed on the home page is configured to automatically connect to a B
 	</div>
 </div>
 <script type="text/javascript">
-	fTelnet.Hostname = 'valhalla.synchro.net';
-	fTelnet.Port = 23;
-    fTelnet.ProxyHostname = '<?xjs write(http_request.vhost); ?>';
-    fTelnet.ProxyPort = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
-    fTelnet.ProxyPortSecure = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
-	fTelnet.ConnectionType = 'telnet';
-	fTelnet.SplashScreen = '<?xjs write(getSplash()); ?>';
-	fTelnet.StatusBarVisible = false;
-	fTelnet.VirtualKeyboardVisible = (
+	Options.Hostname = 'valhalla.synchro.net';
+	Options.Port = 23;
+    Options.ProxyHostname = '<?xjs write(http_request.vhost); ?>';
+    Options.ProxyPort = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
+    Options.ProxyPortSecure = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
+	Options.ConnectionType = 'telnet';
+	Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+	Options.StatusBarVisible = false;
+	Options.VirtualKeyboardVisible = (
 		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
 			navigator.userAgent
 		)
 	);
-	fTelnet.Init();
-	$('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+	var ftClient = new fTelnetClient('fTelnetContainer', Options);
+	$('#ftelnet-connect').click(function() { ftClient.Connect(); });
 </script>
 ```
 
-- 
GitLab


From 993b3bb054974bcbc4e9148558474d4ba2ae48e4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 13 Jul 2017 14:00:28 -0400
Subject: [PATCH 276/752] Updated 'Additional fTelnet targets' example with new
 fTelnet init stuff (Part Two).

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 3c96bd9aa6..e02f847a6d 100644
--- a/README.md
+++ b/README.md
@@ -238,8 +238,8 @@ The fTelnet embed on the home page is configured to automatically connect to a B
 ```
 
 - Edit the first line so that the title of the page (*Some Other BBS*) is to your liking
-- Edit the *fTelnet.Hostname* line so that the text within the quotes points to the system you want to target
-- Edit the *fTelnet.Port* line if the target system's telnet server is listening on some port other than *23*
+- Edit the *Options.Hostname* line so that the text within the quotes points to the system you want to target
+- Edit the *Options.Port* line if the target system's telnet server is listening on some port other than *23*
 
 ### Uninstall
 
-- 
GitLab


From 7a4fe512bec5ab92e16f25f52eda8d18a774971e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 21 Jul 2017 15:55:22 -0400
Subject: [PATCH 277/752] Simplified the way that a list of sidebar modules is
 fetched.

---
 web/lib/sidebar.js | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index a3224c0d83..1042cc23c2 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -9,11 +9,8 @@ function getFileContents(file) {
 }
 
 function getSidebarModules() {
-	return directory(settings.web_root + 'sidebar/*').reduce(
-		function (a, c) {
-			if (!file_isdir(c)) a.push(file_getname(c));
-			return a;
-		}, []
+	return directory(settings.web_root + 'sidebar/*').filter(
+		function (e, i, a) { return (!file_isdir(e)); }
 	);
 }
 
@@ -52,10 +49,10 @@ function writeSidebarModules() {
 		function (module) {
 			if (module.search(/\.xjs\.ssjs$/i) >= 0) return;
 			write('<li class="list-group-item sidebar">');
-			var str = getSidebarModule(settings.web_root + "sidebar/" + module);
+			var str = getSidebarModule(module);
 			if (str !== '') write(str);
 			write('</li>');
 		}
 	);
 	write('</ul>');
-}
\ No newline at end of file
+}
-- 
GitLab


From dc681c5364caefee2a54d28e1b1e4823d4003e28 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 21 Jul 2017 15:56:54 -0400
Subject: [PATCH 278/752] Removed Dovenet from the support section for now, as
 I haven't been keeping up with it lately (whoops).

---
 README.md | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/README.md b/README.md
index e02f847a6d..83fa42e0fa 100644
--- a/README.md
+++ b/README.md
@@ -256,10 +256,6 @@ To stop using this web interface, you can just revert to your previous *web* dir
 - Please browse the existing [issues](https://github.com/echicken/synchronet-web-v4/issues) for this project, including those marked as closed.  You may find that your question has already been asked (and hopefully answered).
 - Open a new [issue](https://github.com/echicken/synchronet-web-v4/issues) here on GitHub
 
-#### On DOVE-Net
-
-- Post a message to *echicken* in [Synchronet Discussion](https://bbs.electronicchicken.com/?page=001-forum.ssjs&sub=sync).  I read this sub regularly and will respond to you there.
-
 #### On IRC
 
 - You can find me in #synchronet on irc.synchro.net.  I may be AFK, but ask your question and idle for a while; I'll respond eventually.
-- 
GitLab


From 070e681af65054f3970800f121d124df7f687ee3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 9 Nov 2017 15:02:22 -0500
Subject: [PATCH 279/752] Don't check for unread mail or telegrams if guest.

---
 web/root/js/common.js | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index 338b9ef46f..0649837d1a 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -102,7 +102,7 @@ function getTelegram() {
 	);
 }
 
-window.onload =	function () { 
+window.onload =	function () {
 
 	$('#button-logout').click(logout);
 	$('#button-login').click(login);
@@ -122,13 +122,17 @@ window.onload =	function () {
 		}
 	);
 
-	setTimeout(scrollUp, 25); 
+	setTimeout(scrollUp, 25);
 	window.onhashchange = scrollUp;
 
-	getMailUnreadCount();
-	setInterval(getMailUnreadCount, updateInterval);
+	if ($('#button-logout').length > 0) {
 
-	getTelegram();
-	setInterval(getTelegram, updateInterval);
+        getMailUnreadCount();
+        setInterval(getMailUnreadCount, updateInterval);
 
-}
\ No newline at end of file
+        getTelegram();
+        setInterval(getTelegram, updateInterval);
+
+    }
+
+}
-- 
GitLab


From d686a63bf5714762e82bafe5cedd1c7f9cae0d6f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 8 Dec 2017 01:07:09 -0500
Subject: [PATCH 280/752] Potentially usable Synchronet BBS list page.  You
 must have a data/sbbslist.json file generated by DM's sbbslist.js.  This page
 is hidden by default.

---
 web/root/pages/More/999-sbbslist.xjs | 117 +++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100644 web/root/pages/More/999-sbbslist.xjs

diff --git a/web/root/pages/More/999-sbbslist.xjs b/web/root/pages/More/999-sbbslist.xjs
new file mode 100644
index 0000000000..900e663400
--- /dev/null
+++ b/web/root/pages/More/999-sbbslist.xjs
@@ -0,0 +1,117 @@
+<!--HIDDEN:Synchronet BBS List-->
+
+<style>
+	.zoomedIn {
+		font-size : 10pt;
+		cursor : zoom-out;
+		line-height : 99%;
+		letter-spacing : -1px;
+	}
+	.zoomedOut {
+		font-size : 2pt;
+		cursor : zoom-in;
+	}
+	pre.ansi {
+		background-color : black;
+	}
+</style>
+
+<script type="text/javascript">
+
+	function zoom(elem) {
+		if ($(elem).hasClass('zoomedIn')) {
+			$(elem).removeClass('zoomedIn');
+			$(elem).addClass('zoomedOut');
+		} else {
+			$(elem).removeClass('zoomedOut');
+			$(elem).addClass('zoomedIn');
+		}
+	}
+
+</script>
+
+<div class="well well-sm"><h3>Synchronet BBS List</h3></div>
+
+<div class="list-group" style="margin-top:1em;">
+<?xjs
+
+	load('graphic.js');
+	var lib = load({}, "sbbslist_lib.js");
+
+	function bbs_preview(bbs) {
+	    var graphic=new Graphic();
+	    graphic.base64_decode(bbs.preview);
+		writeln(
+			'<div class="pull-left" ' +
+			'style="margin-right:5px;" ' +
+			'title="Click to Zoom in">'
+		);
+	    write(
+	    	'<pre class="ansi zoomedOut" onclick="zoom(this)">'
+	    );
+		var html = graphic.HTML;
+		html = html.replace(/background-color: black;/g, '');
+		html = html.replace(/\"color: #a8a8a8;/g, '"');
+		html = html.replace(/\ style=\" \"/g, '');
+		html = html.replace(/<span>([^<]*)<\/span>/g,'$1');
+		write(html);
+	    writeln('</pre>');
+	    writeln('</div>');
+	}
+
+	var list = lib.read_list();
+	if (list === null) list = [];
+	lib.sort(list, "verified_on");
+	list.reverse();
+
+	list.forEach(
+		function (e) {
+			var items = {
+				Location : (typeof e.location === 'string' ? e.location : ''),
+				Sysops : e.sysop.map(function (ee) { return ee.name; }).join(', '),
+				Services : e.service.map(
+					function (ee) {
+						var r = ee.protocol[0].toUpperCase() + ee.protocol.slice(1) + ': ' + ee.address;
+						if (typeof ee.port === 'number') r += ':' + ee.port;
+						return r;
+					}
+				).join ('<br>'),
+				Networks : e.network.map(
+					function (ee) {
+						var r = ee.name;
+						if (typeof ee.address === 'string' && ee.address !== '') {
+							r += ' (' + ee.address + ')';
+						}
+						return r;
+					}
+				).join('<br>'),
+				Software : (typeof e.software !== 'string' ? '' : e.software)
+
+			};
+			writeln('<li class="list-group-item striped">');
+			writeln('<p class="h4">');
+			if (typeof e.web_site === 'string' && e.web_site !== '') {
+				writeln('<a href="http://' + e.web_site + '">' + e.name + '</a>');
+			} else {
+				writeln(e.name);
+			}
+			writeln('</p>');
+			if (e.preview) bbs_preview(e);
+			writeln('<div>' + e.description.join(' ') + '</div><p>');
+			writeln('<table class="table table-condensed table-responsive">');
+			Object.keys(items).forEach(
+				function (ee) {
+					if (items[ee].length < 1) return;
+					writeln(
+						'<tr><td>' + ee + '</td><td>' + items[ee] + '</td></tr>'
+					);
+				}
+			);
+			writeln('</table>');
+			writeln('</li>');
+
+		}
+	);
+
+?>
+</div>
-- 
GitLab


From 0ac5b6e0b20dadc9cb98b4eb5656d2a759162f42 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 12 Dec 2017 22:59:59 -0500
Subject: [PATCH 281/752] Don't expand no fields if you gonna write the header
 again mkay?

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 2c27a70e78..d5c267533e 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -336,7 +336,7 @@ function getMailBody(number) {
 
     var msgBase = new MsgBase('mail');
     if (!msgBase.open()) return ret;
-    var header = msgBase.get_msg_header(number);
+    var header = msgBase.get_msg_header(false, number, false);
     if (header !== null &&
         (   header.to_ext == user.number ||
             header.from_ext == user.number
-- 
GitLab


From 6ab1aff5bdb7a30b0e9e93006f7104c2421cc085 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 17:41:37 -0500
Subject: [PATCH 282/752] Allow for a 'web_root' key in the [web] section of
 ctrl/modopts.ini. This is optional and can point to the place where the
 following directories and files reside:

api/
bootstrap/
css/
error/
images/
js/
pages/
sidebar/
index.xjs

This can be a full or relative path.  It must be at or inside of
your webserver's RootDirectory as specified in the [Web] section
of ctrl/sbbs.ini.
---
 web/lib/init.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index 27e1aea82a..0d8c54377c 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -10,7 +10,13 @@ settings.web_directory = fullpath(
 		: settings.web_directory
 	)
 );
-settings.web_root = backslash(settings.web_directory + 'root/');
+settings.web_root = fullpath(
+	backslash(
+		typeof settings.web_root === 'undefined'
+		? settings.web_directory + 'root'
+		: settings.web_root
+	)
+);
 settings.web_lib = backslash(settings.web_directory + 'lib/');
 
 // Guest
@@ -49,4 +55,4 @@ if (typeof settings.page_size !== 'number' || settings.page_size < 1) {
 
 if (typeof settings.forum_extended_ascii !== 'boolean') {
 	settings.forum_extended_ascii = true;
-}
\ No newline at end of file
+}
-- 
GitLab


From b8558c031da878d8b164026cc3317962ee87cf7c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 17:47:36 -0500
Subject: [PATCH 283/752] Don't load init.js; it's already been done by
 anything that loads this script.

---
 web/lib/auth.js | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index afe8b750e0..f17551d28b 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -1,5 +1,4 @@
 load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
 
 function randomString(length) {
 	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split("");
@@ -89,7 +88,7 @@ function destroySession(cookies) {
 		var cookie = cookies[c].split(',');
 
 		try {
-			
+
 			var usr = new User(cookie[0]);
 			if(usr.number < 1) {
 				throw 'Invalid user number ' + cookie[0] + ' in cookie.';
@@ -140,7 +139,7 @@ function authenticate(alias, password) {
 }
 
 // If someone is trying to log in
-if (typeof http_request.query.username !== 'undefined' && 
+if (typeof http_request.query.username !== 'undefined' &&
 	http_request.query.username[0].length <= LEN_ALIAS &&
 	typeof http_request.query.password != 'undefined' &&
 	http_request.query.password[0].length <= LEN_PASS
-- 
GitLab


From ace538a84e6ccfa860f78436683f70b713dd278a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 18:03:57 -0500
Subject: [PATCH 284/752] Notes on the web_root setting.

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README.md b/README.md
index 83fa42e0fa..afe2e99a12 100644
--- a/README.md
+++ b/README.md
@@ -119,12 +119,15 @@ The following *optional* settings can be added to the [web] section of your *ctr
 	websocket_telnet_port = 1124
 	websocket_rlogin_port = 1514
 	allowed_ftelnet_targets = some.other.bbs:23,yet.another.bbs:2323
+	; Serve this web interface from a subdirectory of the webserver's document root
+	web_root = ../web/root/ecwebv4
 ```
 
 - Setting *forum_extended_ascii* to *false* may resolve problems with displaying UTF-8 encoded characters; character codes > 127 will not be assumed to be extended-ASCII references to CP437 characters.
 - Tweaking *max_messages* may improve forum performance or resolve 'Out of memory' errors, if you see any of those when browsing the forum
 - Normally, fTelnet will try to connect to the websocket proxies based on information from *ctrl/services.ini*.  In some situations (eg. when using an HTTPS reverse proxy) it may be necessary to connect to another port unknown to Synchronet.
 - The *allowed_ftelnet_targets* setting is only necessary if you will be adding additional pages through which users can connect to external systems via fTelnet (see below).  This is a comma-separated list of host:port entries.
+- If you placed the *contents* of the *web/root/* directory from this repository at some other location within your webserver's document root, use the *web_root* value to point to that directory.  (Note that the *contents* of the *web/lib/* directory from this repository must still live at *[web_directory]/lib* on your system.)
 
 ### Customization
 
-- 
GitLab


From d6bf2b0c545e6bad652e7af52d63dfb4a8a093b0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 18:10:01 -0500
Subject: [PATCH 285/752] TOC

---
 README.md | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index afe2e99a12..4eaf4d37cd 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,16 @@
 # synchronet-web-v4
 A web interface for Synchronet BBS
 
+* [Disclaimer](#disclaimer)
+* [Requirements](#requirements)
+* [Quick start](#quick-start)
+* [Configuration](#configuration)
+	* [Advanced / Optional Settings](#optional-settings)
+	* [Additional fTelnet Targets](#additional-ftelnet-targets)
+* [Customization](#customization)
+	* [Sidebar Modules](#sidebar-modules)
+	* [Pages](#pages)
+
 ### Disclaimer
 
 - Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
@@ -106,7 +116,7 @@ if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (co
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
 - Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
 
-#### Additional / advanced settings
+#### Optional Settings
 
 The following *optional* settings can be added to the [web] section of your *ctrl/modopts.ini* file:
 
-- 
GitLab


From 35d6b4f1999e7a0a62a00ada289110221db6c9f2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 18:13:55 -0500
Subject: [PATCH 286/752] Moar TOC

---
 README.md | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4eaf4d37cd..b4dba77b4e 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@ A web interface for Synchronet BBS
 * [Customization](#customization)
 	* [Sidebar Modules](#sidebar-modules)
 	* [Pages](#pages)
+* [Uninstall](#uninstall)
+* [Getting Support](#getting-support)
 
 ### Disclaimer
 
@@ -262,7 +264,7 @@ To stop using this web interface, you can just revert to your previous *web* dir
 - Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
 - Undo any changes you made to your firewall & router during the *Quick Start*
 
-### Support
+### Getting Support
 
 #### Via GitHub
 
@@ -273,6 +275,10 @@ To stop using this web interface, you can just revert to your previous *web* dir
 
 - You can find me in #synchronet on irc.synchro.net.  I may be AFK, but ask your question and idle for a while; I'll respond eventually.
 
+#### On DOVE-Net
+
+- Post a message to 'echicken' in the Synchronet Sysops area.
+
 #### Email
 
 - Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
-- 
GitLab


From 0b563b8e1553d4590f50a1221ef6c1f1dd3a7dab Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 18:42:54 -0500
Subject: [PATCH 287/752] FAQ

---
 README.md | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/README.md b/README.md
index b4dba77b4e..6a5007c463 100644
--- a/README.md
+++ b/README.md
@@ -282,3 +282,21 @@ To stop using this web interface, you can just revert to your previous *web* dir
 #### Email
 
 - Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
+
+### FAQ
+
+#### I followed the instructions, but it isn't working
+
+It usually turns out that the instructions were not followed closely enough.  Please try again.  (I'm not pretending that this thing is bug-free or that the instructions are perfectly clear.  I'm just sayin'.)
+
+#### System performance is poor / memory usage has increased since I installed this
+
+The Forum page is the most likely culprit.  Sorting messages into threads takes up a certain amount of memory and CPU time.  Bots crawling through your Forum may hit several pages at once, etc.  You may be able to mitigate this by playing with the *max_messages* setting as [described above](#optional-settings).  You may also consider consider restricting access to your Forum page so that only authenticated / non-guest users may browse it; access control via *webctrl.ini* files is described in the [Pages](#pages) section above.
+
+#### Why aren't you using *insert flavour-of-the-month web framework here*
+
+Because I don't feel like it, and I don't want to hear about it.  I'm sure it's awesome, though.
+
+#### Why don't you use *insert javascript-tooling-thing here*
+
+Because I don't feel like it.  Also, if it adds additional dependencies or setup steps on behalf of the end-user (sysops), I'm not interested in supporting it.
-- 
GitLab


From edf40f83b7161fb2e7d64a2ff6667a3e07bf7db4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 18:44:32 -0500
Subject: [PATCH 288/752] TOC FAQ link

---
 README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.md b/README.md
index 6a5007c463..6de99fb9f1 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ A web interface for Synchronet BBS
 	* [Pages](#pages)
 * [Uninstall](#uninstall)
 * [Getting Support](#getting-support)
+* [FAQ](#faq)
 
 ### Disclaimer
 
-- 
GitLab


From c06b2ffa9a5d2313916771a4bd9d83c66f5452eb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 18:45:16 -0500
Subject: [PATCH 289/752] New FAQ entry: Q: Something related to Fidonet isn't
 working! A: I don't care.

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 6de99fb9f1..953b07e7d9 100644
--- a/README.md
+++ b/README.md
@@ -294,6 +294,10 @@ It usually turns out that the instructions were not followed closely enough.  Pl
 
 The Forum page is the most likely culprit.  Sorting messages into threads takes up a certain amount of memory and CPU time.  Bots crawling through your Forum may hit several pages at once, etc.  You may be able to mitigate this by playing with the *max_messages* setting as [described above](#optional-settings).  You may also consider consider restricting access to your Forum page so that only authenticated / non-guest users may browse it; access control via *webctrl.ini* files is described in the [Pages](#pages) section above.
 
+#### Something related to Fidonet isn't working!
+
+I don't care.
+
 #### Why aren't you using *insert flavour-of-the-month web framework here*
 
 Because I don't feel like it, and I don't want to hear about it.  I'm sure it's awesome, though.
-- 
GitLab


From 3360fa9984d6622675537c1d02f6b88e63652f8b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jan 2018 20:28:14 -0500
Subject: [PATCH 290/752] Don't show libraries that don't got no directories
 with files in them.  I think.  Untested.

---
 web/lib/files.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/web/lib/files.js b/web/lib/files.js
index 1866cd2de8..3c6e8793dd 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -3,7 +3,11 @@ load('file_size.js');
 
 function listLibraries() {
 	return file_area.lib_list.filter(
-		function (library) { return library.dir_list.length > 0; }
+		function (library) {
+            if (library.dir_list.length < 1) return false;
+            var dirs = listDirectories(library.index);
+            return dirs.length > 0;
+        }
 	);
 }
 
@@ -32,4 +36,4 @@ function listFiles(dir) {
 		}
 	);
 	return files;
-}
\ No newline at end of file
+}
-- 
GitLab


From 91e038dce18606de47a2e6731266560c4b7e1910 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 28 Jan 2018 00:05:48 -0500
Subject: [PATCH 291/752] Server-side avatar loader/cache thing.

---
 web/lib/avatars.js | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 web/lib/avatars.js

diff --git a/web/lib/avatars.js b/web/lib/avatars.js
new file mode 100644
index 0000000000..fc5256dec3
--- /dev/null
+++ b/web/lib/avatars.js
@@ -0,0 +1,38 @@
+const avatar_lib = load({}, 'avatar_lib.js');
+
+function Avatars() {
+
+    const cache = { local : {}, network : {} };
+
+    this.__defineGetter__('cache', function () { return cache; });
+
+    this.get_localuser = function (usernumber) {
+        if (typeof cache.local[usernumber] == 'undefined') {
+            var data = avatar_lib.read_localuser(usernumber);
+            if (!data || data.disabled) {
+                cache.local[usernumber] = false;
+            } else {
+                cache.local[usernumber] = data.data;
+            }
+        } else {
+            return cache.local[usernumber];
+        }
+    }
+
+    this.get_netuser = function (username, netaddr) {
+        if (typeof cache.network[netaddr] == 'undefined') {
+            cache.network[netaddr] = {};
+        }
+        if (typeof cache.network[netaddr][username] == 'undefined') {
+            var data = avatar_lib.read_netuser(username, netaddr);
+            if (!data || data.disabled) {
+                cache.network[netaddr][username] = false;
+            } else {
+                cache.network[netaddr][username] = data.data;
+            }
+        } else {
+            return cache.network[netaddr][username];
+        }
+    }
+
+}
-- 
GitLab


From feb9bc01bb43174fab5f1f261b501e674b539763 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 28 Jan 2018 00:06:50 -0500
Subject: [PATCH 292/752] Client side avatar display thing.

---
 web/root/js/avatars.js | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)
 create mode 100644 web/root/js/avatars.js

diff --git a/web/root/js/avatars.js b/web/root/js/avatars.js
new file mode 100644
index 0000000000..bd04ecb0ce
--- /dev/null
+++ b/web/root/js/avatars.js
@@ -0,0 +1,39 @@
+function Avatarizer() {
+
+    const cache = { local : {}, network : {} };
+    const graphics_converter = new GraphicsConverter('./images/cp437-ibm-vga8.png', 8, 16, 64, 4);
+
+    function populate_image(target, image) {
+        $("[name='avatar-" + target + "']").each(
+            function () { $(this).append($(image).clone()); }
+        );
+    }
+
+    this.get_localuser = function (usernumber, bin) {
+        if (typeof cache.local[usernumber] == 'undefined') {
+            cache.local[usernumber] = null;
+            graphics_converter.from_bin(
+                atob(bin), 10, 6, function (img) {
+                    cache.local[usernumber] = img;
+                    populate_image(usernumber, img);
+                }
+            );
+        }
+    }
+
+    this.get_netuser = function (username, netaddr, bin) {
+        if (typeof cache.network[netaddr] == 'undefined') {
+            cache.network[netaddr] = {};
+        }
+        if (typeof cache.network[netaddr][username] == 'undefined') {
+            cache.network[netaddr][username] = null;
+            graphics_converter.from_bin(
+                atob(bin), 10, 6, function (img) {
+                    cache.network[netaddr][username] = img;
+                    populate_image(username + '@' + netaddr, img);
+                }
+            );
+        }
+    }
+
+}
-- 
GitLab


From a4df70c35f24385f797a4be52ebc6918bf6d8bfc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 28 Jan 2018 00:07:46 -0500
Subject: [PATCH 293/752] Codepage 437 spritesheet, 8x16, 64x4.

---
 web/root/images/cp437-ibm-vga8.png | Bin 0 -> 3886 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 web/root/images/cp437-ibm-vga8.png

diff --git a/web/root/images/cp437-ibm-vga8.png b/web/root/images/cp437-ibm-vga8.png
new file mode 100644
index 0000000000000000000000000000000000000000..e23a9e64a81bc0b52349f6d3cf9abc13f05ef370
GIT binary patch
literal 3886
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
af0#=`{`}v6?mMUh%;4$j=d#Wzp$PzV>3jzO

literal 0
HcmV?d00001

-- 
GitLab


From e14498a07f37ec6b5bdd82b95bf92c1c5a179d83 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 28 Jan 2018 00:08:47 -0500
Subject: [PATCH 294/752] Client-side ANSI to PNG rendering thing. Just does
 .bin for now.

---
 web/root/js/graphics-converter.js | 125 ++++++++++++++++++++++++++++++
 1 file changed, 125 insertions(+)
 create mode 100644 web/root/js/graphics-converter.js

diff --git a/web/root/js/graphics-converter.js b/web/root/js/graphics-converter.js
new file mode 100644
index 0000000000..f339964922
--- /dev/null
+++ b/web/root/js/graphics-converter.js
@@ -0,0 +1,125 @@
+function GraphicsConverter(spritesheet_src, font_width, font_height, spritesheet_columns, spritesheet_rows) {
+
+    const COLORS = [
+        "#000000", // Black
+        "#0000A8", // Blue
+        "#00A800", // Green
+        "#00A8A8", // Cyan
+        "#A80000", // Red
+        "#A800A8", // Magenta
+        "#A85400", // Brown
+        "#A8A8A8", // Light Grey
+        "#545454", // Dark Grey (High Black)
+        "#5454FC", // Light Blue
+        "#54FC54", // Light Green
+        "#54FCFC", // Light Cyan
+        "#FC5454", // Light Red
+        "#FC54FC", // Light Magenta
+        "#FCFC54", // Yellow (High Brown)
+        "#FFFFFF"  // White
+    ];
+
+    function get_workspace(cols, rows, callback) {
+
+        const container = document.createElement('div');
+
+        const canvas = document.createElement('canvas');
+        const ctx = canvas.getContext('2d');
+        canvas.width = cols * font_width;
+        canvas.height = rows * font_height;
+
+        const spritesheet_canvas = document.createElement('canvas');
+        const spritesheet_ctx = spritesheet_canvas.getContext('2d');
+        spritesheet_canvas.width = spritesheet_columns * font_width;
+        spritesheet_canvas.height = spritesheet_rows * font_height;
+
+        container.appendChild(canvas);
+        container.appendChild(spritesheet_canvas);
+
+        const img = new Image();
+        img.addEventListener(
+            'load', function () {
+                spritesheet_ctx.drawImage(img, 0, 0);
+                callback(
+                    {   container : container,
+                        canvas : canvas,
+                        ctx : ctx,
+                        spritesheet_canvas : spritesheet_canvas,
+                        spritesheet_ctx : spritesheet_ctx
+                    }
+                )
+            }
+        );
+        img.src = spritesheet_src;
+
+    }
+
+    function delete_workspace(workspace) {
+        $(workspace.container).remove();
+    }
+
+    // Clip character # 'char' from the spritesheet and return it as ImageData
+    function get_character(workspace, char) {
+        const x = (char * font_width) % (font_width * spritesheet_columns);
+        const y = font_height * Math.floor(char / spritesheet_columns);
+        return workspace.spritesheet_ctx.getImageData(x, y, font_width, font_height);
+    }
+
+    // Draw ImageData 'char' at coordinates 'x', 'y' on the canvas
+    // Colours 'fg' and 'bg' are values from the COLOURS array
+    function put_character(workspace, char, x, y, fg, bg) {
+        // Draw the character
+        workspace.ctx.globalCompositeOperation = 'source-over';
+        workspace.ctx.putImageData(char, x, y);
+        // Overlay it with the foreground colour
+        workspace.ctx.fillStyle = fg;
+        workspace.ctx.globalCompositeOperation = 'source-atop';
+        workspace.ctx.fillRect(x, y, font_width, font_height);
+        // Draw the background colour behind it
+        workspace.ctx.globalCompositeOperation = 'destination-over';
+        workspace.ctx.fillStyle = bg;
+        workspace.ctx.fillRect(x, y, font_width, font_height);
+    }
+
+    function get_png(workspace, callback) {
+        const data = workspace.canvas.toDataURL();
+        const img = new Image();
+        img.addEventListener('load', function () { callback(img); });
+        img.src = data;
+    }
+
+    this.from_bin = function (bin, cols, rows, callback) {
+
+        get_workspace(
+            cols, rows, function (workspace) {
+                var x = 0;
+                var y = 0;
+                for (var n = 0; n < cols * rows * 2; n = n + 2) {
+                    const char = bin.substr(n, 1).charCodeAt(0);
+                    const attr = bin.substr(n + 1, 1).charCodeAt(0);
+                    put_character(
+                        workspace,
+                        get_character(workspace, char),
+                        x * font_width,
+                        y * font_height,
+                        COLORS[attr&15],
+                        COLORS[(attr>>4)&7]
+                    );
+                    x++;
+                    if (x >= cols) {
+                        x = 0;
+                        y++;
+                    }
+                }
+                get_png(
+                    workspace, function (img) {
+                        delete_workspace(workspace);
+                        callback(img);
+                    }
+                );
+            }
+        );
+
+    }
+
+}
-- 
GitLab


From 8c67fe29768c3370100757ee52a51aaec501dcd9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 28 Jan 2018 00:10:04 -0500
Subject: [PATCH 295/752] Use this wacky graphics and avatar stack in the
 forum.

---
 web/root/pages/001-forum.ssjs | 45 +++++++++++++++++++++++++++++++----
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/web/root/pages/001-forum.ssjs b/web/root/pages/001-forum.ssjs
index c5f5a7118b..7296374d5c 100644
--- a/web/root/pages/001-forum.ssjs
+++ b/web/root/pages/001-forum.ssjs
@@ -4,6 +4,7 @@ if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'forum.js');
+load(settings.web_lib + 'avatars.js');
 
 var strings = {
 	script : {
@@ -87,6 +88,7 @@ var strings = {
 			details : {
 				open : '<div class="col-sm-9">',
 				unread : '<span title="Unread" class="glyphicon glyphicon-star"></span>',
+                avatar : '<div name="avatar-%s" class="pull-left" style="padding-right:1em;"></div>',
 				info : 'From <strong>%s</strong>%s to <strong>%s</strong> on %s',
 				close : '</div>'
 			},
@@ -135,7 +137,11 @@ var strings = {
 	}
 };
 
+var avatars = new Avatars();
+
 writeln('<script type="text/javascript" src="./js/forum.js"></script>');
+writeln('<script type="text/javascript" src="./js/graphics-converter.js"></script>');
+writeln('<script type="text/javascript" src="./js/avatars.js"></script>');
 
 if (typeof http_request.query.notice !== 'undefined') {
 	writeln(format(strings.notice_box, http_request.query.notice[0]));
@@ -179,7 +185,16 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		) {
 			writeln(strings.message.header.details.unread);
 			if (firstUnread === '') firstUnread += header.number;
-		}			
+		}
+
+        if (typeof header.from_net_addr != 'undefined') {
+            avatars.get_netuser(header.from, header.from_net_addr);
+            writeln(format(strings.message.header.details.avatar, header.from + '@' + header.from_net_addr));
+        } else {
+            avatars.get_localuser(header.from_ext);
+            writeln(format(strings.message.header.details.avatar, header.from_ext));
+        }
+
 		writeln(
 			format(
 				strings.message.header.details.info,
@@ -222,7 +237,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 			writeln(strings.message.header.voting.poll);
 		}
 		writeln(strings.message.header.voting.close);
-		
+
 		writeln(strings.message.header.close);
 
 		// Body
@@ -418,7 +433,27 @@ if (typeof http_request.query.sub !== 'undefined' &&
 				)
 			);
 		}
-		writeln(strings.script.close);
+        writeln('const avatarizer = new Avatarizer();');
+        Object.keys(avatars.cache.local).forEach(
+            function (e) {
+                const bin = avatars.get_localuser(e);
+                if (!bin) return;
+                writeln('avatarizer.get_localuser("' + e + '", "' + bin + '");');
+            }
+        );
+        Object.keys(avatars.cache.network).forEach(
+            function (e) {
+                Object.keys(avatars.cache.network[e]).forEach(
+                    function (ee) {
+                        const bin = avatars.get_netuser(ee, e);
+                        if (!bin) return;
+                        writeln('avatarizer.get_netuser("' + ee + '", "' + e + '", "' + bin + '");');
+                    }
+                )
+            }
+        );
+        writeln(strings.script.close);
+
 
 	} catch (err) {
 		log(LOG_WARNING, err);
@@ -665,7 +700,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		);
 		writeln(strings.script.close);
 	}
-	
+
 	writeln(format(strings.group_list.breadcrumb, http_request.query.page[0]));
 
 	try {
@@ -678,4 +713,4 @@ if (typeof http_request.query.sub !== 'undefined' &&
 		log(LOG_WARNING, err);
 	}
 
-}
\ No newline at end of file
+}
-- 
GitLab


From db338074d9cb2f92e65edd77e6d055aa01f756b0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 31 Jan 2018 23:14:43 -0500
Subject: [PATCH 296/752] Fulfill prophecy left in a comment several years ago
 and actually search for the appropriate cookie.

---
 mods/websocket-rlogin-service.js | 40 ++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 18 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index e3285e9cda..906ebc48d2 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -236,7 +236,7 @@ var RLoginClient = function(options) {
 
 		if (!state.connected) throw 'RLogin.send: not connected.';
 		if (state.suspendInput) throw 'RLogin.send: input has been suspended.';
-		
+
 		if (typeof data === 'string') {
 			data = data.split('').map(function (d) { return ascii(d); });
 		}
@@ -294,21 +294,21 @@ var RLoginClient = function(options) {
 	}
 
 	this.connect = function () {
-		
+
 		if (typeof options.port !== 'number' ||
 			typeof options.host != 'string'
 		) {
 			throw 'RLogin: invalid host or port argument.';
 		}
-		
+
 		if (typeof options.clientUsername !== 'string') {
 			throw 'RLogin: invalid clientUsername argument.';
 		}
-		
+
 		if (typeof options.serverUsername !== 'string') {
 			throw 'RLogin: invalid serverUsername argument.';
 		}
-		
+
 		if (typeof options.terminalType !== 'string') {
 			throw 'RLogin: invalid terminalType argument.';
 		}
@@ -363,23 +363,27 @@ try {
 
 	wss = new WebSocketProxy(client);
 
-	if (typeof wss.headers['Cookie'] === 'undefined') {
+	if (typeof wss.headers['Cookie'] == 'undefined') {
 		err('No cookie from WebSocket client.');
 	}
 
-	// Should probably search for the right cookie instead of assuming
-	var cookie = wss.headers['Cookie'].split('=');
-	if (cookie[0] !== 'synchronet' || cookie.length < 2) {
-		err('Invalid cookie from WebSocket client.');
-	}
+    var cookie = null;
+    wss.headers['Cookie'].split(';').some(
+        function (e) {
+            if (e.search(/^\s*synchronet\=/) == 0) {
+                cookie = e;
+                return true;
+            } else {
+                return false;
+            }
+        }
+    );
+    if (cookie === null) err('Invalid cookie from WebSocket client.');
+    cookie = cookie.replace(/^\s*synchronet\=/, '').split(',');
 
-	cookie = cookie[1].split(',');
 	cookie[0] = parseInt(cookie[0]);
-	if (cookie.length < 2 ||
-		isNaN(cookie[0]) ||
-		cookie[0] < 1 ||
-		cookie[0] > system.lastuser
-	) {
+	if (cookie.length < 2 || isNaN(cookie[0]) || cookie[0] < 1 || cookie[0] > system.lastuser) {
+        log('cookie ' + JSON.stringify(cookie));
 		err('Invalid cookie from WebSocket client.');
 	}
 
@@ -444,4 +448,4 @@ try {
 } finally {
 	rlogin.disconnect();
 	client.socket.close();
-}
\ No newline at end of file
+}
-- 
GitLab


From b74a348642b4d20d86ae12d1b7a4cdce625210ea Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 1 Feb 2018 01:18:19 -0500
Subject: [PATCH 297/752] Ugly fix for now re: determining address to telnet
 to.  Untested, might work.

---
 mods/websocket-telnet-service.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index b3c2d6c47e..cc10a96b3a 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -229,10 +229,11 @@ try {
     var ini = f.iniGetObject('BBS');
     f.close();
 
-    if (typeof ini.TelnetInterface === 'undefined' || ini.TelnetInterface === '0.0.0.0') {
-        var telnet_addr = '127.0.0.1';
+    if (typeof ini.TelnetInterface === 'undefined') {
+        var telnet_addr = 'localhost';
     } else {
-        var telnet_addr = ini.TelnetInterface;
+        var telnet_addr = ini.TelnetInterface.split(/,/)[0];
+        if (parseInt(telnet_addr.replace(/[^\d]/g, '') == 0)) telnet_addr = 'localhost';
     }
 
     var wss = new WebSocketProxy(client);
-- 
GitLab


From 993e38b2eba01bbacbca2a0fb3b90a72f2d63dc5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 1 Feb 2018 01:24:48 -0500
Subject: [PATCH 298/752] Blah.

---
 mods/websocket-rlogin-service.js | 5 +++--
 mods/websocket-telnet-service.js | 4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index 906ebc48d2..ecd3ca36d5 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -406,10 +406,11 @@ try {
 	var ini = f.iniGetObject('BBS');
 	f.close();
 
-    if (typeof ini.RLoginInterface === 'undefined' || ini.RLoginInterface === '0.0.0.0') {
+    if (typeof ini.RLoginInterface === 'undefined') {
         var rlogin_addr = '127.0.0.1';
     } else {
-        var rlogin_addr = ini.RLoginInterface;
+        var rlogin_addr = ini.RLoginInterface.split(/,/)[0];
+        if (parseInt(rlogin_addr.replace(/[^\d]/g, '') == 0)) rlogin_addr = '127.0.0.1';
     }
 
 	rlogin = new RLoginClient(
diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index cc10a96b3a..6b69242042 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -230,10 +230,10 @@ try {
     f.close();
 
     if (typeof ini.TelnetInterface === 'undefined') {
-        var telnet_addr = 'localhost';
+        var telnet_addr = '127.0.0.1';
     } else {
         var telnet_addr = ini.TelnetInterface.split(/,/)[0];
-        if (parseInt(telnet_addr.replace(/[^\d]/g, '') == 0)) telnet_addr = 'localhost';
+        if (parseInt(telnet_addr.replace(/[^\d]/g, '') == 0)) telnet_addr = '127.0.0.1';
     }
 
     var wss = new WebSocketProxy(client);
-- 
GitLab


From c65bf9c536e3c3267563ab9ba765920645dc19f7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 1 Feb 2018 01:28:15 -0500
Subject: [PATCH 299/752] BLAH.

---
 mods/websocket-rlogin-service.js | 3 ++-
 mods/websocket-telnet-service.js | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
index ecd3ca36d5..172446e01d 100644
--- a/mods/websocket-rlogin-service.js
+++ b/mods/websocket-rlogin-service.js
@@ -410,7 +410,8 @@ try {
         var rlogin_addr = '127.0.0.1';
     } else {
         var rlogin_addr = ini.RLoginInterface.split(/,/)[0];
-        if (parseInt(rlogin_addr.replace(/[^\d]/g, '') == 0)) rlogin_addr = '127.0.0.1';
+        var ra = parseInt(rlogin_addr.replace(/[^\d]/g, ''));
+        if (isNaN(ra) || ra == 0) rlogin_addr = '127.0.0.1';
     }
 
 	rlogin = new RLoginClient(
diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
index 6b69242042..83b1cd5ed8 100644
--- a/mods/websocket-telnet-service.js
+++ b/mods/websocket-telnet-service.js
@@ -233,7 +233,8 @@ try {
         var telnet_addr = '127.0.0.1';
     } else {
         var telnet_addr = ini.TelnetInterface.split(/,/)[0];
-        if (parseInt(telnet_addr.replace(/[^\d]/g, '') == 0)) telnet_addr = '127.0.0.1';
+        var ta = parseInt(telnet_addr.replace(/[^\d]/g, '') == 0);
+        if (isNaN(ta) || ta == 0) telnet_addr = '127.0.0.1';
     }
 
     var wss = new WebSocketProxy(client);
-- 
GitLab


From b3d2bafde6ffee10b89ee3e67cb80b0b811c5858 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Feb 2018 13:38:56 -0500
Subject: [PATCH 300/752] Removed ftelnet stuff

---
 web/root/pages/000-home.xjs | 31 +------------------------------
 1 file changed, 1 insertion(+), 30 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 1eca4b9cb3..215faa48cb 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,31 +1,2 @@
 <!--Home-->
-<?xjs
-	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
-	load(settings.web_lib + 'ftelnet.js');
-	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
-?>
-
-<script src="./ftelnet/ftelnet.norip.xfer.min.js" id="fTelnetScript"></script>
-<style>.fTelnetStatusBar { display : none; }</style>
-<div id="fTelnetContainer" style="margin-bottom:1em;clear:both;"></div>
-<div class="row">
-	<div class="center-block" style="width:200px;margin-bottom:1em;">
-		<button id="ftelnet-connect" class="btn btn-primary">
-			<?xjs write(_hpl.button_ftelnet); ?>
-		</button>
-	</div>
-</div>
-<script type="text/javascript">
-	var Options = new fTelnetOptions();
-	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
-	Options.Port = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;
-	Options.ConnectionType = 'telnet';
-	Options.SplashScreen = '<?xjs write(getSplash()); ?>';
-	Options.VirtualKeyboardVisible = (
-		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
-			navigator.userAgent
-		)
-	);
-	var ftClient = new fTelnetClient('fTelnetContainer', Options);
-	$('#ftelnet-connect').click(function() { ftClient.Connect(); });
-</script>
+Things go here.
-- 
GitLab


From 4000399441fc0eaa28a12e9532d866a7a9daca6b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Feb 2018 13:39:04 -0500
Subject: [PATCH 301/752] Removed.

---
 web/root/pages/003-games.xjs | 97 ------------------------------------
 1 file changed, 97 deletions(-)
 delete mode 100644 web/root/pages/003-games.xjs

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
deleted file mode 100644
index 7edff0c0c8..0000000000
--- a/web/root/pages/003-games.xjs
+++ /dev/null
@@ -1,97 +0,0 @@
-<!--Games-->
-<?xjs
-
-	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
-	load(settings.web_lib + 'ftelnet.js');
-
-	if (typeof settings.xtrn_blacklist === 'string') {
-		settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(',');
-	} else {
-		settings.xtrn_blacklist = [];
-	}
-
-	var xtrn = {};
-	xtrn_area.sec_list.forEach(
-		function (sec) {
-			if (!sec.can_access || sec.prog_list.length < 1) return;
-			if (settings.xtrn_blacklist.indexOf(sec.code.toLowerCase()) > -1) {
-				return;
-			}
-			var x = [];
-			sec.prog_list.forEach(
-				function (prog) {
-					if (!prog.can_access || !prog.can_run) return;
-					if (settings.xtrn_blacklist.indexOf(prog.code.toLowerCase()) > -1) {
-						return;
-					}
-					x.push({ c : prog.code, n : prog.name });
-				}
-			);
-			if (x.length > 0) xtrn[sec.name] = x;
-		}
-	);
-
-?>
-
-<script src="./ftelnet/ftelnet.norip.xfer.min.js" id="fTelnetScript"></script>
-<style>.fTelnetStatusBar { display : none; }</style>
-
-<a name="fTelnet"></a>
-<div id="fTelnetContainer" style="margin-bottom:1em;"></div>
-
-<div id="xtrn-list" class="list-group">
-	<div id="xtrn-list-template" class="list-group-item" style="display:none;">
-		<h4></h4>
-		<ul class="nav nav-pills">
-			<li id="xtrn-item-template" role="presentation" style="display:none;">
-			<a href="#fTelnet"></a>
-			</li>
-		</ul>
-	</div>
-</div>
-
-<script type="text/javascript">
-
-	var Options = new fTelnetOptions();
-	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
-	Options.Port = <?xjs write(settings.websocket_rlogin_port || webSocketRLogin.Port); ?>;
-	Options.ConnectionType = 'telnet';
-	Options.SplashScreen = '<?xjs write(getSplash()); ?>';
-	Options.VirtualKeyboardVisible = (
-		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
-			navigator.userAgent
-		)
-	);
-	var ftClient = new fTelnetClient('fTelnetContainer', Options);
-	ftClient.OnConnectionClose = function () { window.location.reload(); };
-
-	function launchXtrn(code) {
-		$.getJSON(
-			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function(data) { ftClient.Connect(); }
-		);
-	}
-
-	var xtrn = <?xjs write(JSON.stringify(xtrn)); ?>;
-	Object.keys(xtrn).forEach(
-		function (x) {
-			var e = $('#xtrn-list-template').clone();
-			$($(e).find('h4')[0]).text(x);
-			var ul = $($(e).find('ul')[0]);
-			xtrn[x].forEach(
-				function (xx) {
-					var li = $('#xtrn-item-template').clone();
-					var a = $(li).find('a')[0];
-					$(a).text(xx.n);
-					$(a).click(function () { launchXtrn(xx.c); });
-					$(ul).append(li);
-					$(li).show();
-				}
-			);
-			$(e).append(ul);
-			$('#xtrn-list').append(e);
-			$(e).show();
-		}
-	);
-
-</script>
-- 
GitLab


From d44853698210daa41e0dd0501f22e91f819b10f8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Feb 2018 13:39:16 -0500
Subject: [PATCH 302/752] Removed

---
 web/lib/ftelnet.js | 14 --------------
 1 file changed, 14 deletions(-)
 delete mode 100644 web/lib/ftelnet.js

diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
deleted file mode 100644
index 0cf3262471..0000000000
--- a/web/lib/ftelnet.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var f = new File(file_cfgname(system.ctrl_dir, "services.ini"));
-if (!f.open('r')) exit();
-var webSocket = f.iniGetObject('WebSocketTelnet');
-if (webSocket === null) webSocket = f.iniGetObject('WebSocket');
-var webSocketRLogin = f.iniGetObject('WebSocketRLogin');
-f.close();
-
-function getSplash() {
-	var f = new File(settings.ftelnet_splash);
-	f.open('rb');
-	var splash = base64_encode(f.read());
-	f.close();
-	return splash;
-}
\ No newline at end of file
-- 
GitLab


From 04011fe67dd1f42e0147a8236e8717196e80d24b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Feb 2018 13:39:36 -0500
Subject: [PATCH 303/752] Removed

---
 mods/websocket-proxy.js          | 522 -------------------------------
 mods/websocket-rlogin-service.js | 453 ---------------------------
 mods/websocket-telnet-service.js | 284 -----------------
 3 files changed, 1259 deletions(-)
 delete mode 100644 mods/websocket-proxy.js
 delete mode 100644 mods/websocket-rlogin-service.js
 delete mode 100644 mods/websocket-telnet-service.js

diff --git a/mods/websocket-proxy.js b/mods/websocket-proxy.js
deleted file mode 100644
index 0d0d34e1ea..0000000000
--- a/mods/websocket-proxy.js
+++ /dev/null
@@ -1,522 +0,0 @@
-load('sha1.js');
-
-var WebSocketProxy = function(client) {
-
-	var WEBSOCKET_NEED_PACKET_START = 0;
-	var WEBSOCKET_NEED_PAYLOAD_LENGTH = 1;
-	var WEBSOCKET_NEED_MASKING_KEY = 2;
-	var WEBSOCKET_DATA = 3;
-
-	var FFrameMask = [];
-	var FFrameOpCode = 0;
-	var FFramePayloadLength = 0;
-	var FFramePayloadReceived = 0;
-	var FWebSocketState = WEBSOCKET_NEED_PACKET_START;
-
-	var self = this;
-	this.headers = [];
-
-	var ClientDataBuffer = []; // From client
-	var ServerDataBuffer = []; // From server
-
-	function CalculateWebSocketKey(InLine) {
-		var Digits = '';
-		var Spaces = 0;
-		for (var i = 0; i < InLine.length; i++) {
-			if (InLine.charAt(i) == ' ') {
-				Spaces++;
-			} else if (!isNaN(InLine.charAt(i))) {
-				Digits += InLine.charAt(i);
-			}
-		}
-		return Digits / Spaces;
-	}
-
-	function GetFromWebSocketClient() {
-		switch (self.headers['Version']) {
-			case 0: return GetFromWebSocketClientDraft0();
-			case 7: 
-			case 8: 
-			case 13:
-				return GetFromWebSocketClientVersion7();
-		}
-	}
-
-	function GetFromWebSocketClientDraft0() {
-		var Result = [];
-		var InByte = 0;
-		var InByte2 = 0;
-		var InByte3 = 0;
-		
-		while (client.socket.data_waiting) {
-			InByte = client.socket.recvBin(1);
-			
-			// Check what the client packet state is
-			switch (FWebSocketState) {
-				case WEBSOCKET_NEED_PACKET_START:
-					// Check for 0x00 to indicate the start of a data packet
-					if (InByte === 0x00) {
-						FWebSocketState = WEBSOCKET_DATA;
-					}
-					break;
-				case WEBSOCKET_DATA:
-					// We're in a data packet, so check for 0xFF, which
-					// indicates the data packet is done
-					if (InByte == 0xFF) {
-						FWebSocketState = WEBSOCKET_NEED_PACKET_START;
-					} else {
-						// Check if the byte needs to be UTF-8 decoded
-						if (InByte < 128) {
-							Result.push(InByte);
-						} else if ((InByte > 191) && (InByte < 224)) {
-							// Handle UTF-8 decode
-							InByte2 = client.socket.recvBin(1);
-							Result.push(((InByte & 31) << 6) | (InByte2 & 63));
-						} else {
-							// Handle UTF-8 decode (should never need this, but
-							// included anyway)
-							InByte2 = client.socket.recvBin(1);
-							InByte3 = client.socket.recvBin(1);
-							Result.push(
-								((InByte&15)<<12)|((InByte2&63)<<6)|(InByte3&63)
-							);
-						}
-					}
-					break;
-			}
-		}
-		
-		return Result;
-	}
-
-	function GetFromWebSocketClientVersion7() {
-		var Result = [];
-		var InByte = 0;
-		var InByte2 = 0;
-		var InByte3 = 0;
-
-		while (client.socket.data_waiting) {
-			// Check what the client packet state is
-			switch (FWebSocketState) {
-				case WEBSOCKET_NEED_PACKET_START:
-					// Next byte will give us the opcode, and also tell is if
-					// the message is fragmented
-					FFrameMask = [];
-					FFrameOpCode = client.socket.recvBin(1);
-					FFramePayloadLength = 0;
-					FFramePayloadReceived = 0;
-					FWebSocketState = WEBSOCKET_NEED_PAYLOAD_LENGTH;
-					break;
-				case WEBSOCKET_NEED_PAYLOAD_LENGTH:
-					FFramePayloadLength = (client.socket.recvBin(1) & 0x7F);
-					if (FFramePayloadLength === 126) {
-						FFramePayloadLength = client.socket.recvBin(2);
-					} else if (FFramePayloadLength === 127) {
-						FFramePayloadLength = client.socket.recvBin(8);
-					}
-					FWebSocketState = WEBSOCKET_NEED_MASKING_KEY;
-					break;
-				case WEBSOCKET_NEED_MASKING_KEY:
-					InByte = client.socket.recvBin(4);
-					FFrameMask[0] = (InByte & 0xFF000000) >> 24;
-					FFrameMask[1] = (InByte & 0x00FF0000) >> 16;
-					FFrameMask[2] = (InByte & 0x0000FF00) >> 8;
-					FFrameMask[3] = InByte & 0x000000FF;
-					FWebSocketState = WEBSOCKET_DATA;
-					break;
-				case WEBSOCKET_DATA:
-					InByte = (
-						client.socket.recvBin(1)^FFrameMask[
-							FFramePayloadReceived++ % 4
-						]
-					);
-
-					// Check if the byte needs to be UTF-8 decoded
-					if ((InByte & 0x80) === 0) {
-						Result.push(InByte);
-					} else if ((InByte & 0xE0) === 0xC0) {
-						// Handle UTF-8 decode
-						InByte2 = (
-							client.socket.recvBin(1)^FFrameMask[
-								FFramePayloadReceived++ % 4
-							]
-						);
-						Result.push(((InByte & 31) << 6) | (InByte2 & 63));
-					} else {
-						log(LOG_NOTICE, 'Byte out of range: ' + InByte);
-					}
-
-					// Check if we've received the full payload
-					if (FFramePayloadReceived === FFramePayloadLength) {
-						FWebSocketState = WEBSOCKET_NEED_PACKET_START;
-					}
-					break;
-			}
-		}
-
-		return Result;
-	}
-
-	function SendToWebSocketClient(AData) {
-		switch (self.headers['Version']) {
-			case 0: 
-				SendToWebSocketClientDraft0(AData); 
-				break;
-			case 7: 
-			case 8: 
-			case 13:
-				SendToWebSocketClientVersion7(AData); 
-				break;
-		}
-	}
-
-	function SendToWebSocketClientDraft0(AData) {
-		// Send 0x00 to indicate the start of a data packet
-		client.socket.sendBin(0x00, 1);
-
-		for (var i = 0; i < AData.length; i++) {
-			// Check if the byte needs to be UTF-8 encoded
-			if ((AData[i] & 0xFF) <= 127) {
-				client.socket.sendBin(AData[i], 1);
-			} else if ((AData[i] & 0xFF) <= 2047) {
-				// Handle UTF-8 encode
-				client.socket.sendBin((AData[i] >> 6) | 192, 1);
-				client.socket.sendBin((AData[i] & 63) | 128, 1);
-			} else {
-				log(LOG_NOTICE, 'Byte out of range: ' + AData[i]);
-			}
-		}
-
-		// Send 0xFF to indicate the end of a data packet
-		client.socket.sendBin(0xFF, 1);
-	}
-
-	function SendToWebSocketClientVersion7(AData) {
-		if (AData.length > 0) {
-			var ToSend = [];
-
-			for (var i = 0; i < AData.length; i++) {
-				// Check if the byte needs to be UTF-8 encoded
-				if ((AData[i] & 0xFF) <= 127) {
-					ToSend.push(AData[i]);
-				} else if (
-					((AData[i] & 0xFF) >= 128) && ((AData[i] & 0xFF) <= 2047)
-				) {
-					// Handle UTF-8 encode
-					ToSend.push((AData[i] >> 6) | 192);
-					ToSend.push((AData[i] & 63) | 128);
-				} else {
-					log(LOG_NOTICE, 'Byte out of range: ' + AData[i]);
-				}
-			}
-
-			client.socket.sendBin(0x81, 1);
-			if (ToSend.length <= 125) {
-				client.socket.sendBin(ToSend.length, 1);
-			} else if (ToSend.length <= 65535) {
-				client.socket.sendBin(126, 1);
-				client.socket.sendBin(ToSend.length, 2);
-			} else {
-				// NOTE: client.socket.sendBin(ToSend.length, 8); didn't work,
-				// so this modification limits the send to 2^32 bytes (probably
-				// not an issue). Probably should look into a proper fix at
-				// some point though
-				client.socket.sendBin(127, 1);
-				client.socket.sendBin(0, 4);
-				client.socket.sendBin(ToSend.length, 4);
-			}
-			
-			for (var i = 0; i < ToSend.length; i++) {
-				client.socket.sendBin(ToSend[i] & 0xFF, 1);
-			}
-		}
-	}
-
-	function ShakeHands() {
-		self.headers['Version'] = 0;
-
-		try {
-			// Keep reading header data until we get all the data we want
-			while (true) {
-				// Read another line, abort if we don't get one in 5 seconds
-				var InLine = client.socket.recvline(1024, 5);
-				if (InLine === null) {
-					log(LOG_ERR,
-						'Timeout exceeded while waiting for complete handshake'
-					);
-					return false;
-				}
-
-				log(LOG_DEBUG, 'Handshake Line: ' + InLine);
-				
-				// Check for blank line (indicates we have most of the header,
-				// and only the last 8 bytes remain
-				if (InLine === '') {
-					switch (self.headers['Version']) {
-						case 0: 
-							return ShakeHandsDraft0();
-						case 7: 
-						case 8: 
-						case 13:
-							return ShakeHandsVersion7();
-						default: 
-							// TODO If this version does not match a version
-							// understood by the server, the server MUST abort
-							// the websocket handshake described in this section
-							// and instead send an appropriate HTTP error code
-							// (such as 426 Upgrade Required), and a
-							// |Sec-WebSocket-Version| header indicating the
-							// version(s) the server is capable of
-							// understanding.
-							break;
-					}
-					break;
-				} else if (InLine.indexOf('Connection:') === 0) {
-					// Example: "Connection: Upgrade"
-					self.headers['Connection'] = InLine.replace(
-						/Connection:\s?/i,
-						''
-					);
-				} else if (InLine.indexOf('GET') === 0) {
-					// Example: "GET /demo HTTP/1.1"
-					var GET = InLine.split(' ');
-					self.headers['Path'] = GET[1];
-				} else if (InLine.indexOf('Host:') === 0) {
-					// Example: "Host: example.com"
-					self.headers['Host'] = InLine.replace(/Host:\s?/i, '');
-				} else if (InLine.indexOf('Origin:') === 0) {
-					// Example: "Origin: http://example.com"
-					self.headers['Origin'] = InLine.replace(/Origin:\s?/i, '');
-				} else if (InLine.indexOf('Sec-WebSocket-Key:') === 0) {
-					// Example: "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=="
-					self.headers['Key'] = InLine.replace(
-						/Sec-WebSocket-Key:\s?/i,
-						''
-					);
-				} else if (InLine.indexOf('Sec-WebSocket-Key1:') === 0) {
-					// Example: "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5"
-					self.headers['Key1'] = CalculateWebSocketKey(
-						InLine.replace(/Sec-WebSocket-Key1:\s?/i, '')
-					);
-				} else if (InLine.indexOf('Sec-WebSocket-Key2:') === 0) {
-					// Example: "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00"
-					self.headers['Key2'] = CalculateWebSocketKey(
-						InLine.replace(/Sec-WebSocket-Key2:\s?/i, '')
-					);
-				} else if (InLine.indexOf('Sec-WebSocket-Origin:') === 0) {
-					// Example: "Sec-WebSocket-Origin: http://example.com"
-					self.headers['Origin'] = InLine.replace(
-						/Sec-WebSocket-Origin:\s?/i,
-						''
-					);
-				} else if (InLine.indexOf('Sec-WebSocket-Protocol:') === 0) {
-					// Example: "Sec-WebSocket-Protocol: sample"
-					self.headers['SubProtocol'] = InLine.replace(
-						/Sec-WebSocket-Protocol:\s?/i,
-						''
-					);
-				} else if (InLine.indexOf('Sec-WebSocket-Draft') === 0) {
-					// Example: "Sec-WebSocket-Draft: 2"
-					try {
-						self.headers['Version'] = parseInt(
-							InLine.replace(/Sec-WebSocket-Draft:\s?/i, '')
-						);
-					} catch (err) {
-						self.headers['Version'] = 0;
-					}
-				} else if (InLine.indexOf('Sec-WebSocket-Version') === 0) {
-					// Example: "Sec-WebSocket-Version: 8"
-					try {
-						self.headers['Version'] = parseInt(
-							InLine.replace(/Sec-WebSocket-Version:\s?/i, '')
-						);
-					} catch (err) {
-						self.headers['Version'] = 0;
-					}
-				} else if (InLine.indexOf('Upgrade:') === 0) {
-					// Example: "Upgrade: websocket"
-					self.headers['Upgrade'] = InLine.replace(/Upgrade:\s?/i,'');
-				} else if (InLine.indexOf('Cookie:') === 0) {
-				 	self.headers['Cookie'] = InLine.replace(/Cookie:\s?/i, '');
-				}
-			}
-		} catch (err) {
-			log(LOG_ERR, 'ShakeHands() error: ' + err.toString());
-		}
-		
-		return false;
-	}
-
-	function ShakeHandsDraft0() {
-		// Ensure we have all the data we need
-		if (('Key1' in self.headers) &&
-			('Key2' in self.headers) &&
-			('Host' in self.headers) &&
-			('Origin' in self.headers !== '') &&
-			('Path' in self.headers)
-		) {
-			// Combine Key1, Key2, and the last 8 bytes into a string that we
-			// will later hash
-			var ToHash = ''
-			ToHash += String.fromCharCode(
-				(self.headers['Key1'] & 0xFF000000) >> 24
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key1'] & 0x00FF0000) >> 16
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key1'] & 0x0000FF00) >> 8
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key1'] & 0x000000FF) >> 0
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key2'] & 0xFF000000) >> 24
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key2'] & 0x00FF0000) >> 16
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key2'] & 0x0000FF00) >> 8
-			);
-			ToHash += String.fromCharCode(
-				(self.headers['Key2'] & 0x000000FF) >> 0
-			);
-			for (var i = 0; i < 8; i++) {
-				ToHash += String.fromCharCode(client.socket.recvBin(1));
-			}
-			
-			// Hash the string
-			var Hashed = md5_calc(ToHash, true);
-
-			// Setup the handshake response
-			var Response = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
-						   'Upgrade: WebSocket\r\n' +
-						   'Connection: Upgrade\r\n' +
-						   'Sec-WebSocket-Origin: ' + self.headers['Origin'] +
-						   '\r\n' +
-						   'Sec-WebSocket-Location: ws://' +
-						   self.headers['Host'] +
-						   self.headers['Path'] +
-						   '\r\n';
-			if ('SubProtocol' in self.headers) {
-				Response +=
-					'Sec-WebSocket-Protocol: ' +
-					self.headers['SubProtocol'] +
-					'\r\n';
-			}
-			Response += '\r\n';
-			
-			// Loop through the hash string (which is hex encoded) and append
-			// the individual bytes to the response
-			for (var i = 0; i < Hashed.length; i += 2) {
-				Response += String.fromCharCode(
-					parseInt(Hashed.charAt(i) + Hashed.charAt(i + 1), 16)
-				);
-			}
-			
-			// Send the response and return
-			client.socket.send(Response);
-			return true;
-		} else {
-			// We're missing some pice of data, log what we do have
-			log(LOG_ERR,
-				'Missing some piece of handshake data.  Here\'s what we have:'
-			);
-			for(var x in self.headers) { 
-				log(LOG_ERR, x + ' => ' + self.headers[x]); 
-			}
-			return false;
-		}
-	}
-
-	function ShakeHandsVersion7() {
-		// Ensure we have all the data we need
-		if (('Key' in self.headers) &&
-			('Host' in self.headers) &&
-			('Origin' in self.headers !== '') &&
-			('Path' in self.headers)
-		) {
-			var AcceptGUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
-
-			// Combine Key and GUID
-			var ToHash = self.headers['Key'] + AcceptGUID;
-			
-			// Hash the string
-			var Hashed = Sha1.hash(ToHash, false);
-
-			// Encode the hash
-			var ToEncode = '';
-			for (var i = 0; i <= 38; i += 2) {
-				ToEncode += String.fromCharCode(
-					parseInt(Hashed.substr(i, 2), 16)
-				);
-			}
-			var Encoded = base64_encode(ToEncode);
-
-			// Setup the handshake response
-			var Response = 'HTTP/1.1 101 Switching Protocols\r\n' +
-						   'Upgrade: websocket\r\n' +
-						   'Connection: Upgrade\r\n' +
-						   'Sec-WebSocket-Accept: ' + Encoded + '\r\n';
-			if ('SubProtocol' in self.headers) {
-				// Only sub-protocol we support
-				Response += 'Sec-WebSocket-Protocol: plain\r\n';
-			}
-			Response += '\r\n';
-			
-			// Send the response and return
-			client.socket.send(Response);
-			return true;
-		} else {
-			log(LOG_ERR,
-				'Missing some piece of handshake data.  Here\'s what we have:'
-			);
-			for(var x in self.headers) { 
-				log(LOG_ERR, x + ' => ' + self.headers[x]); 
-			}
-			return false;
-		}
-	}
-
-	this.__defineGetter__(
-		'data_waiting',
-		function() {
-			return (ClientDataBuffer.length > 0);
-		}
-	);
-
-	this.send = function(data) {
-		if (typeof data === 'string') {
-			data = data.split('').map(
-				function (d) {
-					return ascii(d);
-				}
-			);
-		}
-		ServerDataBuffer = ServerDataBuffer.concat(data);
-	}
-
-	this.receive = function() {
-		var data = '';
-		while (ClientDataBuffer.length > 0) {
-			data += ascii(ClientDataBuffer.shift());
-		}
-		return data;
-	}
-
-	this.receiveArray = function(len) {
-		return ClientDataBuffer.splice(
-			0,
-			(typeof len === 'number' ? len : ClientDataBuffer.length)
-		);
-	}
-
-	this.cycle = function() {
-		ClientDataBuffer = ClientDataBuffer.concat(GetFromWebSocketClient());
-		SendToWebSocketClient(ServerDataBuffer.splice(0, 4096));
-	}
-
-	if(!ShakeHands()) throw 'ShakeHands() failed';
-
-}
\ No newline at end of file
diff --git a/mods/websocket-rlogin-service.js b/mods/websocket-rlogin-service.js
deleted file mode 100644
index 172446e01d..0000000000
--- a/mods/websocket-rlogin-service.js
+++ /dev/null
@@ -1,453 +0,0 @@
-load('sbbsdefs.js');
-load('websocket-proxy.js');
-
-function err(msg) {
-	log(LOG_DEBUG, msg);
-	client.socket.close();
-	exit();
-}
-
-function getSession(un) {
-	var fn = format('%suser/%04d.web', system.data_dir, un);
-	if (!file_exists(fn)) return false;
-	var f = new File(fn);
-	if (!f.open('r')) return false;
-	var session = f.iniGetObject();
-	f.close();
-	return session;
-}
-
-var RLoginClient = function(options) {
-
-	var self = this;
-
-	const	CAN = 0x18,
-			CR = 0x0D,
-			DC1 = 0x11,
-			DC3 = 0x13,
-			DOT = 0x2E,
-			EOM = 0x19,
-			EOT = 0x04,
-			LF = 0x0A,
-			SUB = 0x1A,
-			DISCARD = 0x02,
-			RAW = 0x10,
-			COOKED = 0x20,
-			WINDOW = 0x80;
-
-	var serverBuffer = []; // From server
-	var clientBuffer = []; // From client
-
-	var state = {
-		connected : false,
-		cooked : true,
-		suspendInput : false,
-		suspendOutput : false,
-		watchForClientEscape : true,
-		clientHasEscaped : false
-	};
-
-	var properties = {
-		rows : 24,
-		columns : 80,
-		pixelsX : 640,
-		pixelsY : 480,
-		clientEscape : '~'
-	};
-
-	// As suggested by RFC1282
-	var clientEscapes = {
-		DOT : self.disconnect,
-		EOT : self.disconnect,
-		SUB : function() {
-			state.suspendInput = (state.suspendInput) ? false : true;
-			state.suspendOutput = state.suspendInput;
-		},
-		EOM : function() {
-			state.suspendInput = (state.suspendInput) ? false : true;
-			state.suspendOutput = false;
-		}
-	};
-
-	this.__defineGetter__('connected', function () { return state.connected; });
-
-	this.__defineSetter__(
-		'connected',
-		function (value) {
-			if (typeof value === 'boolean' && !value) self.disconnect();
-		}
-	);
-
-	this.__defineGetter__('rows', function () { return properties.rows; });
-
-	this.__defineSetter__(
-		'rows',
-		function(value) {
-			if (typeof value === 'number' && value > 0) {
-				properties.rows = value;
-			} else {
-				throw 'RLogin: Invalid \'rows\' setting ' + value;
-			}
-		}
-	);
-
-	this.__defineGetter__(
-		'columns',
-		function () { return properties.columns; }
-	);
-
-	this.__defineSetter__(
-		'columns',
-		function (value) {
-			if (typeof value === 'number' && value > 0) {
-				properties.columns = value;
-			} else {
-				throw 'RLogin: Invalid \'columns\' setting ' + value;
-			}
-		}
-	);
-
-	this.__defineGetter__(
-		'pixelsX',
-		function () { return properties.pixelsX; }
-	);
-
-	this.__defineSetter__(
-		'pixelsX',
-		function (value) {
-			if (typeof value === 'number' && value > 0) {
-				properties.pixelsX = value;
-			} else {
-				throw 'RLogin: Invalid \'pixelsX\' setting ' + value;
-			}
-		}
-	);
-
-	this.__defineGetter__(
-		'pixelsY',
-		function () { return properties.pixelsY; }
-	);
-
-	this.__defineSetter__(
-		'pixelsY',
-		function (value) {
-			if (typeof value === 'number' && value > 0) {
-				properties.pixelsY = value;
-			} else {
-				throw 'RLogin: Invalid \'pixelsY\' setting ' + value;
-			}
-		}
-	);
-
-	this.__defineGetter__(
-		'clientEscape',
-		function() { return properties.clientEscape; }
-	);
-
-	this.__defineSetter__(
-		'clientEscape',
-		function (value) {
-			if (typeof value === 'string' && value.length === 1) {
-				properties.clientEscape = value;
-			} else {
-				throw 'RLogin: Invalid \'clientEscape\' setting ' + value;
-			}
-		}
-	);
-
-	var handle = new Socket();
-
-	function getServerData() {
-
-		if (handle.nread < 1) return;
-
-		var data = [];
-		while (handle.nread > 0) {
-			data.push(handle.recvBin(1));
-		}
-
-		if (!state.connected) {
-			if (data[0] === 0) {
-				state.connected = true;
-				if (data.length > 1) {
-					data = data.slice(1);
-				} else {
-					return;
-				}
-			} else {
-				self.disconnect();
-			}
-		}
-
-		// If I could tell if the TCP urgent-data pointer had been set,
-		// I would uncomment (and complete) this block.  We'll settle
-		// for a partial implementation for the time being.
-		// We would need something to tell us if urgent data was sent,
-		// eg. var lookingForControlCode = urgentDataPointerIsSet();
-		/*
-		var temp = [];
-		for (var d = 0; d < data.length; d++) {
-			if (!lookingForControlCode) {
-				temp.push(data[d]);
-				continue;
-			}
-			switch (data[d]) {
-				case DISCARD:
-					temp = [];
-					// We found our control code
-					lookingForControlCode = false;
-					break;
-				case RAW:
-					state.cooked = false;
-					lookingForControlCode = false;
-					break;
-				case COOKED:
-					state.cooked = true;
-					lookingForControlCode = false;
-					break;
-				case WINDOW:
-					self.sendWCCS();
-					lookingForControlCode = false;
-					break;
-				default:
-					temp.push(data[d]);
-					break;
-			}
-		}
-		if (!state.suspendOutput) self.emit('data', new Buffer(temp));
-		*/
-		if (!state.suspendOutput) serverBuffer = serverBuffer.concat(data);
-	}
-
-	// Send a Window Change Control Sequence
-	// this.sendWCCS = function() {
-	// 	var magicCookie = [0xFF, 0xFF, 0x73, 0x73];
-	// 	var rcxy = new Buffer(8);
-	// 	rcxy.writeUInt16LE(properties.rows, 0);
-	// 	rcxy.writeUInt16LE(properties.columns, 2);
-	// 	rcxy.writeUInt16LE(properties.pixelsX, 4);
-	// 	rcxy.writeUInt16LE(properties.pixelsY, 6);
-	// 	if(state.connected)
-	// 		handle.write(Buffer.concat([magicCookie, rcxy]));
-	// }
-
-	// Send 'data' (String or Buffer) to the rlogin server
-	this.send = function (data) {
-
-		if (!state.connected) throw 'RLogin.send: not connected.';
-		if (state.suspendInput) throw 'RLogin.send: input has been suspended.';
-
-		if (typeof data === 'string') {
-			data = data.split('').map(function (d) { return ascii(d); });
-		}
-
-		var temp = [];
-		for (var d = 0; d < data.length; d++) {
-			if (state.watchForClientEscape &&
-				data[d] == properties.clientEscape.charCodeAt(0)
-			) {
-				state.watchForClientEscape = false;
-				state.clientHasEscaped = true;
-				continue;
-			}
-			if (state.clientHasEscaped) {
-				state.clientHasEscaped = false;
-				if (typeof clientEscapes[data[d]] !== 'undefined') {
-					clientEscapes[data[d]]();
-				}
-				continue;
-			}
-			if (state.cooked && (data[d] === DC1 || data[d] === DC3)) {
-				state.suspendOutput = (data[d] === DC3);
-				continue;
-			}
-			if ((d > 0 && data[d - 1] === CR && data[d] === LF) ||
-				data[d] == CAN
-			) {
-				state.watchForClientEscape = true;
-			}
-			temp.push(data[d]);
-		}
-		clientBuffer = clientBuffer.concat(temp);
-
-	}
-
-	this.receive = function () {
-		return serverBuffer.splice(0, serverBuffer.length);
-	}
-
-	/*	If 'ch' is found in client input immediately after the
-		'this.clientEscape' character when:
-			- this is the first input after connection establishment or
-			- these are the first characters on a new line or
-			- these are the first characters after a line-cancel character
-		then the function 'callback' will be called.  Use this to allow
-		client input to trigger a particular action.	*/
-	this.addClientEscape = function (ch, callback) {
-		if(	(typeof ch !== 'string' && typeof ch !== 'number') ||
-			(typeof ch === 'string' && ch.length > 1) ||
-			typeof callback !== 'function'
-		) {
-			throw 'RLogin.addClientEscape: invalid arguments.';
-		}
-		clientEscapes[ch.charCodeAt(0)] = callback;
-	}
-
-	this.connect = function () {
-
-		if (typeof options.port !== 'number' ||
-			typeof options.host != 'string'
-		) {
-			throw 'RLogin: invalid host or port argument.';
-		}
-
-		if (typeof options.clientUsername !== 'string') {
-			throw 'RLogin: invalid clientUsername argument.';
-		}
-
-		if (typeof options.serverUsername !== 'string') {
-			throw 'RLogin: invalid serverUsername argument.';
-		}
-
-		if (typeof options.terminalType !== 'string') {
-			throw 'RLogin: invalid terminalType argument.';
-		}
-
-		if (typeof options.terminalSpeed !== 'number') {
-			throw 'RLogin: invalid terminalSpeed argument.';
-		}
-
-		if (handle.connect(options.host, options.port)) {
-			handle.sendBin(0, 1);
-			handle.send(options.clientUsername);
-			handle.sendBin(0, 1);
-			handle.send(options.serverUsername);
-			handle.sendBin(0, 1);
-			handle.send(options.terminalType + '/' + options.terminalSpeed);
-			handle.sendBin(0, 1);
-			while (handle.is_connected && handle.nread < 1) {
-				mswait(5);
-			}
-			getServerData();
-		} else {
-			throw 'RLogin: Unable to connect to server.';
-		}
-
-	}
-
-	this.cycle = function () {
-
-		if (!handle.is_connected) {
-			state.connected = false;
-			return;
-		}
-
-		getServerData();
-
-		if (state.suspendInput || clientBuffer.length < 1) return;
-
-		while (clientBuffer.length > 0) {
-			handle.sendBin(clientBuffer.shift(), 1);
-		}
-
-	}
-
-	this.disconnect = function () {
-		handle.close();
-		state.connected = false;
-	}
-
-}
-
-try {
-
-	wss = new WebSocketProxy(client);
-
-	if (typeof wss.headers['Cookie'] == 'undefined') {
-		err('No cookie from WebSocket client.');
-	}
-
-    var cookie = null;
-    wss.headers['Cookie'].split(';').some(
-        function (e) {
-            if (e.search(/^\s*synchronet\=/) == 0) {
-                cookie = e;
-                return true;
-            } else {
-                return false;
-            }
-        }
-    );
-    if (cookie === null) err('Invalid cookie from WebSocket client.');
-    cookie = cookie.replace(/^\s*synchronet\=/, '').split(',');
-
-	cookie[0] = parseInt(cookie[0]);
-	if (cookie.length < 2 || isNaN(cookie[0]) || cookie[0] < 1 || cookie[0] > system.lastuser) {
-        log('cookie ' + JSON.stringify(cookie));
-		err('Invalid cookie from WebSocket client.');
-	}
-
-	var usr = new User(cookie[0]);
-	var session = getSession(usr.number);
-	if (!session) {
-		err('Unable to read web session file for user #' + usr.number);
-	}
-	if (cookie[1] != session.key) {
-		err('Session key mismatch for user #' + usr.number);
-	}
-	if (typeof session.xtrn !== 'string' ||
-		typeof xtrn_area.prog[session.xtrn] === 'undefined'
-	) {
-		err('Invalid external program code.');
-	}
-
-	var f = new File(file_cfgname(system.ctrl_dir, 'sbbs.ini'));
-	if (!f.open('r')) err('Unable to open sbbs.ini.');
-	var ini = f.iniGetObject('BBS');
-	f.close();
-
-    if (typeof ini.RLoginInterface === 'undefined') {
-        var rlogin_addr = '127.0.0.1';
-    } else {
-        var rlogin_addr = ini.RLoginInterface.split(/,/)[0];
-        var ra = parseInt(rlogin_addr.replace(/[^\d]/g, ''));
-        if (isNaN(ra) || ra == 0) rlogin_addr = '127.0.0.1';
-    }
-
-	rlogin = new RLoginClient(
-		{	host : rlogin_addr,
-			port : ini.RLoginPort,
-			clientUsername : usr.security.password,
-			serverUsername : usr.alias,
-			terminalType : 'xtrn=' + session.xtrn,
-			terminalSpeed : 115200
-		}
-	);
-	rlogin.connect();
-	log(LOG_DEBUG, usr.alias + ' logged on via RLogin for ' + session.xtrn);
-
-	while (client.socket.is_connected && rlogin.connected) {
-
-		wss.cycle();
-		rlogin.cycle();
-
-		var send = rlogin.receive();
-		if (send.length > 0) wss.send(send);
-
-		while (wss.data_waiting) {
-			var data = wss.receiveArray();
-			rlogin.send(data);
-		}
-
-		mswait(5);
-
-	}
-
-} catch (er) {
-
-	log(LOG_ERR, er);
-
-} finally {
-	rlogin.disconnect();
-	client.socket.close();
-}
diff --git a/mods/websocket-telnet-service.js b/mods/websocket-telnet-service.js
deleted file mode 100644
index 83b1cd5ed8..0000000000
--- a/mods/websocket-telnet-service.js
+++ /dev/null
@@ -1,284 +0,0 @@
-load('sbbsdefs.js');
-load('websocket-proxy.js');
-load('modopts.js');
-
-function log_err(msg) {
-	log(LOG_DEBUG, msg);
-	client.socket.close();
-	exit();
-}
-
-var TelnetClient = function(host, port) {
-
-    var MAX_BUFFER = 32767;
-
-    var TELNET_DATA = 0;
-    var TELNET_IAC = 1;
-    var TELNET_SUBNEGOTIATE = 2;
-    var TELNET_SUBNEGOTIATE_IAC = 3;
-    var TELNET_WILL = 4;
-    var TELNET_WONT = 5;
-    var TELNET_DO = 6;
-    var TELNET_DONT = 7;
-
-    var TELNET_CMD_TTYLOC = 28;
-
-    var state = TELNET_DATA;
-
-    var buffers = {
-        rx : [], // From server
-        tx : [] // To server
-    };
-
-    var socket = new Socket();
-    socket.connect(host, port);
-
-    this.__defineGetter__(
-        'connected',
-        function () {
-            return socket.is_connected;
-        }
-    );
-
-    this.__defineSetter__(
-        'connected',
-        function (bool) {
-            if (!bool && socket.is_connected) {
-                socket.close();
-            } else if (bool && !socket.is_connected) {
-                socket.connect(host, port);
-            }
-        }
-    );
-
-    this.__defineGetter__(
-        'data_waiting',
-        function () {
-            return (buffers.rx.length > 0);
-        }
-    );
-
-    function receive() {
-
-        var rx = [];
-
-        while (socket.data_waiting && rx.length < MAX_BUFFER) {
-            var nr = (socket.nread >= 4 ? 4 : (socket.nread >= 2 ? 2 : 1));
-            var bin = socket.recvBin(nr);
-            if (nr === 4) {
-                rx.push((bin&(255<<24))>>>24);
-                rx.push((bin&(255<<16))>>>16);
-            }
-            if (nr >= 2) rx.push((bin&(255<<8))>>>8);
-            rx.push(bin&255);
-        }
-
-        while (rx.length > 0) {
-            var b = rx.shift();
-            switch (state) {
-                case TELNET_DATA:
-                    if (b == 0xFF) {
-                        state = TELNET_IAC;
-                    } else {
-                        buffers.rx.push(b);
-                    }
-                    break;
-                case TELNET_IAC:
-                    switch (b) {
-                        case 0xF1: // NOP: No operation.
-                        case 0xF2: // Data Mark: The data stream portion of a Synch. This should always be accompanied by a TCP Urgent notification.
-                        case 0xF3: // Break: NVT character BRK.
-                        case 0xF4: // Interrupt Process: The function IP.
-                        case 0xF5: // Abort output: The function AO.
-                        case 0xF6: // Are You There: The function AYT.
-                        case 0xF7: // Erase character: The function EC.
-                        case 0xF8: // Erase Line: The function EL.
-                        case 0xF9: // Go ahead: The GA signal
-                            // Ignore these single byte commands
-                            state = TELNET_DATA;
-                            break;
-                        case 0xFA: // Subnegotiation
-                            state = TELNET_SUBNEGOTIATE;
-                            break;
-                        case 0xFB: // Will
-                            state = TELNET_WILL;
-                            break;
-                        case 0xFC: // Wont
-                            state = TELNET_WONT;
-                            break;
-                        case 0xFD: // Do
-                            state = TELNET_DO;
-                            break;
-                        case 0xFE: // Dont
-                            state = TELNET_DONT;
-                            break;
-                        case 0xFF:
-                            buffers.rx.push(0xFF);
-                            state = TELNET_DATA;
-                            break;
-                    }
-                    break;
-                case TELNET_SUBNEGOTIATE:
-                    if (b == 0xFF) state = TELNET_SUBNEGOTIATE_IAC;
-                    break;
-                case TELNET_SUBNEGOTIATE_IAC:
-                    if (b == 0xFF) {
-                        state = TELNET_SUBNEGOTIATE;
-                    } else if (b == 0xF0) {
-                        state = TELNET_DATA;
-                    } else {
-                        // Unexpected
-                        state = TELNET_DATA;
-                    }
-                    break;
-                case TELNET_DO:
-                    switch (b) {
-                        // This will bork with IPV6
-                        case TELNET_CMD_TTYLOC:
-                            socket.sendBin(255, 1);
-                            socket.sendBin(250, 1);
-                            socket.sendBin(28, 1);
-                            socket.sendBin(0, 1);
-                            client.ip_address.split('.').forEach(
-                                function (e) {
-                                    e = parseInt(e);
-                                    socket.sendBin(e, 1);
-                                    if (e === 255) socket.sendBin(e, 1);
-                                }
-                            );
-                            client.ip_address.split('.').forEach(
-                                function (e) {
-                                    e = parseInt(e);
-                                    socket.sendBin(e, 1);
-                                    if (e === 255) socket.sendBin(e, 1);
-                                }
-                            );
-                            socket.sendBin(255, 1);
-                            socket.sendBin(240, 1);
-                            break;
-                        default:
-                            break;
-                    }
-                    state = TELNET_DATA;
-                    break;
-                case TELNET_WILL:
-                case TELNET_WONT:
-                case TELNET_DONT:
-                    state = TELNET_DATA;
-                    break;
-                default:
-                    break;
-            }
-        }
-
-    }
-
-    function transmit() {
-
-        if (!socket.is_connected || buffers.tx.length < 1) return;
-
-        while (buffers.tx.length >= 4) {
-            var chunk = (buffers.tx.shift()<<24);
-            chunk |= (buffers.tx.shift()<<16);
-            chunk |= (buffers.tx.shift()<<8);
-            chunk |= buffers.tx.shift();
-            socket.sendBin(chunk, 4);
-        }
-
-        if (buffers.tx.length >= 2) {
-            var chunk = (buffers.tx.shift()<<8);
-            chunk |= buffers.tx.shift();
-            socket.sendBin(chunk, 2);
-        }
-
-        if (buffers.tx.length > 0) {
-            socket.sendBin(buffers.tx.shift(), 1);
-        }
-
-    }
-
-    this.receive = function () {
-        return buffers.rx.splice(0, buffers.rx.length);
-    }
-
-    this.send = function (arr) {
-
-        if (typeof arr === 'string') {
-            var arr = arr.map(
-                function (e) {
-                    return ascii(e);
-                }
-            );
-        }
-
-        buffers.tx = buffers.tx.concat(arr);
-
-    }
-
-    this.cycle = function () {
-        receive();
-        transmit();
-    }
-
-}
-
-try {
-
-    var f = new File(file_cfgname(system.ctrl_dir, 'sbbs.ini'));
-    if (!f.open('r')) log_err('Unable to open sbbs.ini.');
-    var ini = f.iniGetObject('BBS');
-    f.close();
-
-    if (typeof ini.TelnetInterface === 'undefined') {
-        var telnet_addr = '127.0.0.1';
-    } else {
-        var telnet_addr = ini.TelnetInterface.split(/,/)[0];
-        var ta = parseInt(telnet_addr.replace(/[^\d]/g, '') == 0);
-        if (isNaN(ta) || ta == 0) telnet_addr = '127.0.0.1';
-    }
-
-    var wss = new WebSocketProxy(client);
-	var wsspath = wss.headers.Path.split('/');
-	if (wsspath.length < 3 || isNaN(parseInt(wsspath[2]))) {
-		var telnet = new TelnetClient(telnet_addr, ini.TelnetPort);
-	} else {
-		var _settings = get_mod_options('web');
-		if (typeof _settings.allowed_ftelnet_targets !== 'string') {
-			throw 'Client supplied Path but no allowed_ftelnet_targets supplied in modopts.ini [web] section.';
-		}
-		var targets = _settings.allowed_ftelnet_targets.split(',');
-		if (!targets.some(function (e) { var target = e.split(':'); return target[0] === wsspath[1] && target[1] === wsspath[2]; })) {
-			throw 'Client supplied Path is not in allowed_ftelnet_targets list.';
-		}
-		log('Using client-supplied target ' + wsspath[1] + ':' + wsspath[2]);
-		var telnet = new TelnetClient(wsspath[1], parseInt(wsspath[2]));
-	}
-
-	while (client.socket.is_connected && telnet.connected) {
-
-    	wss.cycle();
-        telnet.cycle();
-
-        if (telnet.data_waiting) {
-            var data = telnet.receive();
-            wss.send(data);
-        }
-
-		while (wss.data_waiting) {
-			var data = wss.receiveArray();
-            telnet.send(data);
-		}
-
-        mswait(5);
-
-	}
-
-} catch (err) {
-
-	log(LOG_ERR, 'Caught: ' + err);
-
-} finally {
-
-	client.socket.close();
-
-}
-- 
GitLab


From 452f523e19ad89b5fb788def3daf2a45fba33077 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Feb 2018 13:43:14 -0500
Subject: [PATCH 304/752] Removed fTelnet references and instructions.

---
 README.md | 103 +-----------------------------------------------------
 1 file changed, 1 insertion(+), 102 deletions(-)

diff --git a/README.md b/README.md
index 953b07e7d9..344c3d7942 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,6 @@ A web interface for Synchronet BBS
 * [Quick start](#quick-start)
 * [Configuration](#configuration)
 	* [Advanced / Optional Settings](#optional-settings)
-	* [Additional fTelnet Targets](#additional-ftelnet-targets)
 * [Customization](#customization)
 	* [Sidebar Modules](#sidebar-modules)
 	* [Pages](#pages)
@@ -32,10 +31,6 @@ A web interface for Synchronet BBS
 	- Copy the contents of the downloaded *mods* directory into your local *mods* directory
 	- Copy the contents of the downloaded *text* directory into your local *text* directory
 	- Rename your current *web* directory to something like *web.old* and then copy the downloaded *web* directory in its place
-- [Download fTelnet](https://github.com/rickparrish/fTelnet/archive/master.zip)
-	- Extract the archive
-	- Copy the *release* subdirectory into your new *web/root/* directory
-	- Rename the copied *release* subdirectory to *ftelnet*
 - Add the following section to your *ctrl/modopts.ini* file:
 ```ini
 [web]
@@ -54,16 +49,12 @@ A web interface for Synchronet BBS
 	maximum_telegram_length = 800
 	; Where (absolute or relative to 'exec') the 'lib' and 'root' directories live
 	web_directory = ../web
-	; Path to a .ans file to use as the ftelnet splash screen
-	ftelnet_splash = ../text/synch.ans
 	; Enable or disable keyboard navigation in message threads
 	keyboard_navigation = false
 	; Display upvote/downvote buttons in message threads (3.17)
 	vote_functions = true
 	; Refresh nodelist, vote counts, etc. this often (in milliseconds)
 	refresh_interval = 60000
-	; External Programs (or entire sections) to exclude from the Games page
-	xtrn_blacklist = scfg,oneliner
 	; Disable the sidebar altogether
 	layout_sidebar_off = false
 	; Place the sidebar on the left-hand side of the page
@@ -71,53 +62,14 @@ A web interface for Synchronet BBS
 	; Make the page consume the entire width of the browser window
 	layout_full_width = false
 ```
-- Add the following section to your *ctrl/services.ini* file:
-```ini
-[WebSocketTelnet]
-Port=1123
-Options=NO_HOST_LOOKUP
-Command=websocket-telnet-service.js
-```
-- Add the following section to your *ctrl/services.ini* file:
-```ini
-[WebSocketRLogin]
-Port=1513
-Options=NO_HOST_LOOKUP
-Command=websocket-rlogin-service.js
-```
+
 - Tell your router and firewall to open and forward ports *1123* and *1513* to your BBS
 - If you were running ecWeb v3 and modified the *RootDirectory* value in the *[Web]* section of *ctrl/sbbs.ini* to point to *../web/root/ecwebv3*, change it back to *../web/root*.
-
-- Edit the *[logon]* section of your *ctrl/modopts.ini* file, and ensure that it has an *rlogin_auto_xtrn* key with a value of *true*
-	- NB: that's the *[logon]* section and not the *[login]* section
-
-```ini
-[logon]
-rlogin_auto_xtrn = true
-```
-
-- Your *logon.js* file should have a block of code near the top that looks like this, but if it doesn't you should add it in:
-
-```js
-var options = load("modopts.js", "logon");
-
-// Check if we're being asked to auto-run an external (web interface external programs section uses this)
-if (options && (options.rlogin_auto_xtrn) && (bbs.sys_status & SS_RLOGIN) && (console.terminal.indexOf("xtrn=") === 0)) {
-    var external_code = console.terminal.substring(5);
-    if (!bbs.exec_xtrn(external_code)) {
-        alert(log(LOG_ERR,"!ERROR Unable to launch external: '" + external_code + "'"));
-    }
-    bbs.hangup();
-	exit();
-}
-```
-
 - Start your BBS back up again
 
 ### Configuration
 
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
-- Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
 
 #### Optional Settings
 
@@ -128,18 +80,12 @@ The following *optional* settings can be added to the [web] section of your *ctr
 	forum_extended_ascii = false
 	; Only load this many messages from each sub (default: 0 for all)
 	max_messages = 500
-	; Connect to the websocket proxies on ports other than specified in ctrl/services.ini
-	websocket_telnet_port = 1124
-	websocket_rlogin_port = 1514
-	allowed_ftelnet_targets = some.other.bbs:23,yet.another.bbs:2323
 	; Serve this web interface from a subdirectory of the webserver's document root
 	web_root = ../web/root/ecwebv4
 ```
 
 - Setting *forum_extended_ascii* to *false* may resolve problems with displaying UTF-8 encoded characters; character codes > 127 will not be assumed to be extended-ASCII references to CP437 characters.
 - Tweaking *max_messages* may improve forum performance or resolve 'Out of memory' errors, if you see any of those when browsing the forum
-- Normally, fTelnet will try to connect to the websocket proxies based on information from *ctrl/services.ini*.  In some situations (eg. when using an HTTPS reverse proxy) it may be necessary to connect to another port unknown to Synchronet.
-- The *allowed_ftelnet_targets* setting is only necessary if you will be adding additional pages through which users can connect to external systems via fTelnet (see below).  This is a comma-separated list of host:port entries.
 - If you placed the *contents* of the *web/root/* directory from this repository at some other location within your webserver's document root, use the *web_root* value to point to that directory.  (Note that the *contents* of the *web/lib/* directory from this repository must still live at *[web_directory]/lib* on your system.)
 
 ### Customization
@@ -210,53 +156,6 @@ You can add drop-down menus to the navigation bar by adding subdirectories to th
 - Subdirectories with names beginning with *.* will be ignored.
 - Each subdirectory can contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file for access control.
 
-#### Additional fTelnet targets
-
-The fTelnet embed on the home page is configured to automatically connect to a BBS on the same system that is hosting the website.  If you run another BBS or wish to allow users of your website to connect via fTelnet to some other BBS:
-
-- Edit *ctrl/modopts.ini*
-- In the *[web]* section, add the *allowed_ftelnet_targets* key if it doesn't already exist (see *Additional/advanced settings* above)
-- Add an entry to the list for each additional host you want to allow fTelnet to connect to
-- Create a new *.xjs* file in your *pages/* directory or in a subdirectory thereof.  (In this example, I've created */sbbs/web/root/pages/More/006-some-other-bbs.xjs*.)  Populate it with the following content:
-
-```html
-<!--Some Other BBS-->
-<?xjs
-	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
-	load(settings.web_lib + 'ftelnet.js');
-?>
-
-<script src="./ftelnet/ftelnet.norip.xfer.min.js" id="fTelnetScript"></script>
-<style>.fTelnetStatusBar { display : none; }</style>
-<div id="fTelnetContainer" style="margin-bottom:1em;clear:both;"></div>
-<div class="row">
-	<div class="center-block" style="width:200px;margin-bottom:1em;">
-		<button id="ftelnet-connect" class="btn btn-primary">Connect via Telnet</button>
-	</div>
-</div>
-<script type="text/javascript">
-	Options.Hostname = 'valhalla.synchro.net';
-	Options.Port = 23;
-    Options.ProxyHostname = '<?xjs write(http_request.vhost); ?>';
-    Options.ProxyPort = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
-    Options.ProxyPortSecure = <?xjs write(settings.websocket_telnet_port || webSocket.Port); ?>;;
-	Options.ConnectionType = 'telnet';
-	Options.SplashScreen = '<?xjs write(getSplash()); ?>';
-	Options.StatusBarVisible = false;
-	Options.VirtualKeyboardVisible = (
-		/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
-			navigator.userAgent
-		)
-	);
-	var ftClient = new fTelnetClient('fTelnetContainer', Options);
-	$('#ftelnet-connect').click(function() { ftClient.Connect(); });
-</script>
-```
-
-- Edit the first line so that the title of the page (*Some Other BBS*) is to your liking
-- Edit the *Options.Hostname* line so that the text within the quotes points to the system you want to target
-- Edit the *Options.Port* line if the target system's telnet server is listening on some port other than *23*
-
 ### Uninstall
 
 To stop using this web interface, you can just revert to your previous *web* directory at any time.
-- 
GitLab


From d837dc129f38b47f1cdc061191852f09b4e8fa59 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 25 Feb 2018 00:38:39 -0500
Subject: [PATCH 305/752] Clean up some sorta backward validation of reg form
 fields. If a parameter was submitted and is valid, use it; if it wasn't
 submitted or was invalid - and is required - complain.

---
 web/root/api/register.ssjs | 90 +++++++++++++++++---------------------
 1 file changed, 41 insertions(+), 49 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index a9dd602cca..8954a1f96d 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -42,8 +42,9 @@ function required(mask) {
 function cleanParam(param) {
 	if (paramExists(param)) {
 		return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
-	}
-	return "";
+	} else {
+	   return "";
+    }
 }
 
 function paramExists(param) {
@@ -51,8 +52,9 @@ function paramExists(param) {
 		http_request.query[param][0] !== ''
 	) {
 		return true;
-	}
-	return false;
+	} else {
+	    return false;
+    }
 }
 
 function paramLength(param) {
@@ -147,63 +149,53 @@ if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
 	prepUser.netmail = cleanParam('netmail');
 }
 
-if (required(UQ_REALNAME) &&
-	(	!paramExists('realname') ||
-		paramLength('realname') < MIN_REALNAME ||
-		paramLength('realname') > LEN_NAME
-	)
+if (paramExists('realname') &&
+    paramLength('realname') >= MIN_REALNAME &&
+    paramLength('realname') <= LEN_NAME
 ) {
-	reply.errors.push(_rl.error_invalid_name);
-} else {
-	prepUser.name = cleanParam('realname');
+    prepUser.name = cleanParam('realname');
+} else if (required(UQ_REALNAME)) {
+    reply.errors.push(_rl.error_invalid_name);
 }
 
-if (required(UQ_LOCATION) &&
-	(	!paramExists('location') ||
-		paramLength('location') < MIN_LOCATION ||
-		paramLength('location') > LEN_LOCATION
-	)
+if (paramExists('location') &&
+    paramLength('location') >= MIN_LOCATION &&
+    paramLength('location') <= LEN_LOCATION
 ) {
-	reply.errors.push(_rl.error_invalid_location);
-} else {
-	prepUser.location = cleanParam('location');
+    prepUser.location = cleanParam('location');
+} else if (required(UQ_LOCATION)) {
+    reply.errors.push(_rl.error_invalid_location);
 }
 
-if (required(UQ_ADDRESS) &&
-	(	!paramExists('address') ||
-		paramLength('address') < MIN_ADDRESS ||
-		paramLength('address') > LEN_ADDRESS ||
-		!paramExists('zipcode') ||
-		paramLength('zipcode') < 3 ||
-		paramLength('zipcode') > LEN_ADDRESS
-	)
+if (paramExists('address') &&
+    paramLength('address') >= MIN_ADDRESS &&
+    paramLength('address') <= LEN_ADDRESS &&
+    paramExists('zipcode') &&
+    paramLength('zipcode') >= 3 &&
+    paramLength('zipcode') <= LEN_ADDRESS
 ) {
-	reply.errors.push(_rl.error_invalid_street_address);
-} else {
-	prepUser.address = cleanParam('address');
-	prepUser.zipcode = cleanParam('zipcode');
+    prepUser.address = cleanParam('address');
+    prepUser.zipcode = cleanParam('zipcode');
+} else if (required(UQ_ADDRESS)) {
+    reply.errors.push(_rl.error_invalid_street_address);
 }
 
-if (required(UQ_PHONE) &&
-	(	!paramExists('phone') ||
-		paramLength('phone') < MIN_PHONE ||
-		paramLength('phone') > LEN_PHONE
-	)
+if (paramExists('phone') &&
+    paramLength('phone') >= MIN_PHONE &&
+    paramLength('phone') <= LEN_PHONE
 ) {
-	reply.errors.push(_rl.error_invalid_phone);
-} else {
-	prepUser.phone = cleanParam('phone');
+    prepUser.phone = cleanParam('phone');
+} else if (required(UQ_PHONE)) {
+    reply.errors.push(_rl.error_invalid_phone);
 }
 
-if (required(UQ_SEX) &&
-	(	!paramExists('gender') ||
-		paramLength('gender') != 1 ||
-		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
-	)
+if (paramExists('gender') &&
+    paramLength('gender') == 1 &&
+    ['X', 'M', 'F', 'O'].indexOf(http_request.query.gender[0] > -1)
 ) {
-	reply.errors.push(_rl.error_invalid_gender);
-} else {
-	prepUser.gender = http_request.query.gender[0];
+    prepUser.gender = http_request.query.gender[0];
+} else if (required(UQ_SEX)) {
+    reply.errors.push(_rl.error_invalid_gender);
 }
 
 if (paramExists('birth') &&
@@ -220,4 +212,4 @@ if (reply.errors.length < 1) newUser();
 reply = JSON.stringify(reply);
 http_reply.header['Content-Type'] = 'application/json';
 http_reply.header['Content-Length'] = reply.length;
-write(reply);
\ No newline at end of file
+write(reply);
-- 
GitLab


From f411361fc3db0c2317379d082e67b1328e6cff92 Mon Sep 17 00:00:00 2001
From: rickparrish <github@rick.parrish>
Date: Tue, 13 Mar 2018 19:00:30 -0400
Subject: [PATCH 306/752] Add embed-based fTelnet

---
 README.md                    |   5 ++
 web/lib/ftelnet.js           |  12 +++
 web/root/pages/000-home.xjs  |  62 +++++++++++++++-
 web/root/pages/003-games.xjs | 138 +++++++++++++++++++++++++++++++++++
 4 files changed, 216 insertions(+), 1 deletion(-)
 create mode 100644 web/lib/ftelnet.js
 create mode 100644 web/root/pages/003-games.xjs

diff --git a/README.md b/README.md
index 344c3d7942..b41855c9bd 100644
--- a/README.md
+++ b/README.md
@@ -49,12 +49,16 @@ A web interface for Synchronet BBS
 	maximum_telegram_length = 800
 	; Where (absolute or relative to 'exec') the 'lib' and 'root' directories live
 	web_directory = ../web
+    ; Path to a .ans file to use as the ftelnet splash screen
+    ftelnet_splash = ../text/synch.ans
 	; Enable or disable keyboard navigation in message threads
 	keyboard_navigation = false
 	; Display upvote/downvote buttons in message threads (3.17)
 	vote_functions = true
 	; Refresh nodelist, vote counts, etc. this often (in milliseconds)
 	refresh_interval = 60000
+    ; External Programs (or entire sections) to exclude from the Games page
+    xtrn_blacklist = scfg,oneliner
 	; Disable the sidebar altogether
 	layout_sidebar_off = false
 	; Place the sidebar on the left-hand side of the page
@@ -70,6 +74,7 @@ A web interface for Synchronet BBS
 ### Configuration
 
 - Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
+- Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
 
 #### Optional Settings
 
diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
new file mode 100644
index 0000000000..9673a5fe39
--- /dev/null
+++ b/web/lib/ftelnet.js
@@ -0,0 +1,12 @@
+function getSplash() {
+    var DefaultSplash = 'G1swbQ0KG1syOEMbWzE7MzBtICAgG1swbRtbMTlDG1szNG3cG1sxbdwbWzBtICAgG1szNG3cDQobWzM3bRtbMTNDG1sxbSAbWzMzbSAbWzBtG1sxM0MbWzM0bdwbWzM3bSAgG1szNG3c3LAbWzQ0OzMwbbIbWzQwOzM3bRtbMTFDG1szNG0gIBtbMW3c3xtbMG0bWzlDG1sxOzMwbWdqL9rkDQobWzBtG1s2QxtbMW1zeW5jaHJvbmV0G1swbSAgIBtbMTszMG3c3BtbMG0gG1sxOzMwbd8bWzBtICAgG1szNG3+G1szN20bWzVDG1szNG0gIN+y29zcG1szN20gICAbWzM0bdzcICAgG1sxbd4bWzA7MzRt3Q0KG1szN20bWzdDG1szNG3cG1szN20gG1szNG3+G1szN20gICAg3NzcG1sxbbGxsBtbMG0gICAgG1szNG3c3Nzc2xtbMTs0NG3cICD+G1swOzM0bdvc3tsbWzQ0OzMwbbAbWzQwOzM0bbLfIN4bWzE7NDRtsLAbWzA7MzRtsbDcG1sxbdwbWzQ2bbIbWzA7MzRtIBtbMzdt3xtbMTszMG3fG1swbdsbWzE7NDdt3Nzc3NwbWzBtsiAgIBtbMzRt3xtbMzdtICAbWzM0bd/f3NwNChtbMzdtICAgIBtbMTszNG3cG1swOzM0bd8bWzM3bSAgINwbWzFt3BtbNDdt3BtbNDBt29sbWzQ3bdvb27EbWzQwbbAbWzBtICAbWzM0bdwbWzE7NDRt3NwbWzQwbdvf3xtbMDszNG3fG1szN20g3NzcIBtbMzRt3BtbMTs0NG2wG1swOzM0bdvfG1szN20g3NzcIBtbMzRt3htbMTs0NG2yG1swOzM0bdwbWzQ0OzMybbAbWzQ2OzM0bbIbWzE7NDBt398bWzA7MzRt3xtbMzZtIBtbMzdtICAbWzE7NDc7MzBt3BtbMzdtstvb2xtbNDBtsRtbMG0gINzc3Nzc3CAbWzE7MzRtsN0NChtbMG0gIBtbMTszNG0g3htbMDszNG3dG1szN20gICAbWzFtsBtbNDdtstvb29/f29uyG1swbd0gG1sxOzM0bbEbWzQ2bbIbWzQ3bbIbWzQ2bbIbWzQwbbIbWzBtIBtbMTs0N23e29vbG1s0MG2xG1swbSAbWzE7MzRt3BtbNDRtshtbMDszNG3fG1szN23cG1sxOzQ3bbCx29uyG1swbd0gG1s0NjszNG3cG1sxOzQwbd8bWzBtIBtbMTszMG3cG1swbdwbWzE7NDdtsLCwsLAbWzBt3NsbWzE7NDdtstvbshtbNDBtsBtbMG0g2xtbMTs0N23b27AbWzBtIBtbMzRt3N8bWzFt39zcG1swOzM0bdwNChtbMzdtICAgIBtbNDY7MzBtshtbNDA7MzRtICAbWzE7MzBt3htbNDc7MzdtsbLb29wbWzBt3CDfG1sxOzQ3bd/fG1swbdvcIBtbMTszNG3f3xtbMDszNG0gG1sxbbEbWzBtIBtbMTs0N23e29sbWzQwbbIbWzBt3RtbMTszNG3eG1s0Nm2yG1swbSAbWzE7MzBt3BtbMG2y3xtbMTs0N22xstvbG1s0MG2xG1swbSAg3BtbMTs0N22wsbKysrKysrEbWzBtIBtbMTs0NzszMG2wG1szN23b29sbWzQwbbEbWzBtIN4bWzE7NDdt3tuyG1swbd0bWzM0bd8bWzE7NDRtsBtbNDBt2xtbNDdtsrIbWzQwbbEbWzA7MzRtsrEbWzM3bSAbWzM0bbANCiAgG1sxbSAbWzA7MzRt3NwbWzE7NDRtsBtbMDszNG3cG1szN20gG1sxOzMwbd8bWzBt3xtbMTs0N23f39vb3BtbMG3cICAbWzE7MzBt3BtbMG3c3NwbWzFtsBtbMzBt3RtbMzRtsBtbMG0gG1sxOzQ3bd7b2xtbNDBtsbAbWzBt3BtbMzZt3xtbMzdtICAbWzM0bdwbWzM3bSCwG1sxOzQ3bbLbsrAbWzBtIBtbMW2wG1s0N22xstvb39vb2xtbNDBtshtbMG3dIBtbMTszMG3eG1s0NzszN22x29uxG1swbdzc2xtbMTs0N23b2xtbNDBtstzcG1swbdwbWzFt3BtbMG3c3CDcIBtbMTszNG2wG1swOzQ0OzMwbbIbWzBtDQobWzM0bSAg2xtbMTs0NG2wshtbMDszNG0gG1sxOzQ0bbEbWzQwbbEbWzA7NDY7MzRtshtbNDBt3NwbWzM3bSDf3xtbMTs0N23f39wbWzBt3CAbWzE7NDdtsdvb2xtbMG3dIBtbMTszMG3eG1s0NzszN23e29sbWzQwbbCyG1s0N23b29wbWzQwbdwbWzBt3BtbMzRtIBtbMTszN20gG1s0N22ysrEgG1s0MDszMG3eG1s0NzszN22xstvbG1swbd8gG1sxbbAbWzQ3bdvb3RtbMG0gG1szNG3+IBtbMTszN22wG1s0N22y29vb29vb27KxG1swbSAbWzM0bdwbWzFt3BtbMDszNm3cG1sxOzM0bdwbWzA7MzRt3NwbWzFt3BtbMDszNG3fDQog3htbNDQ7MzZtsBtbNDY7MzRtshtbMTs0MG3d3t/fG1swbSDc3NwgG1szNG3cG1szN20g3tsbWzE7NDdt3BtbMG3bG1sxbbEbWzQ3bbLb29wbWzBt3N4bWzE7NDdt29uyG1swbd0bWzE7NDdtsbLb29vb3BtbMG3cG1sxOzQ3bbKxsBtbMG3bG1sxOzMwbd4bWzQ3OzM3bbCy27EbWzBtIBtbMzRtINwbWzE7MzBt3xtbMG3fG1sxbd/fG1szMG0gG1swbSDeG1sxOzQ3bbCysRtbMG3dICDbG1sxOzQ3bbKyshtbMG3dIBtbMzRt3xtbMW3f3BtbNDZtshtbMG0NChtbMzRtICDfG1sxbdsbWzQ2bd+yG1s0MG3dG1swbbAbWzQ3OzMwbbAbWzQwOzM3bdsbWzFtsRtbMG3c3NzcG1sxOzQ3bSAbWzQwbbGxG1s0N20gG1swbdwbWzE7NDc7MzBt3BtbNDA7Mzdt3xtbNDdt39/b29vbsRtbMG0gG1sxOzQ3bbCxsbAbWzBtIBtbMW3f2xtbNDdt29uysBtbMG3bIBtbMTs0NzszMG3cG1szN22wsbAbWzBt29wgG1szNG3f3xtbMzdtINyxG1sxOzMwbd0bWzBtINsbWzE7NDdtsbEbWzBt3RtbMzRt3twbWzM3bd4bWzE7NDdtsbGxG1swbdsgG1sxOzQ0OzM2bbAbWzQwOzM0bd/fDQobWzBtG1s1QxtbMTszNG0gG1swOzM0bSAbWzM3bbAbWzQ3OzMwbbAbWzQwOzM3bdvb2xtbMTs0N23bsrGwsCAbWzBt29vbG1sxOzQ3OzMwbd8bWzQwbdwbWzBtIN4bWzE7NDdtsrKyG1swbd3e2xtbMTs0N22wsBtbMG3b3SAgG1sxbd/bG1s0N23bsbAbWzBt3SDfG1sxOzQ3bSAbWzQwbbGxG1s0N20g3BtbMG3bG1sxOzQ3bf4bWzBt29uyG1sxbd0bWzA7MzRt3BtbMzdt3htbMTs0N22wG1swbdsbWzFt3RtbMzRt3htbMDs0NDszNm2wG1s0MDszN23eG1sxOzQ3bbCwsBtbMG3bG1sxbd0bWzA7MzZt3t0NChtbMzdtICAgIBtbMzZtsBtbNDY7MzBtshtbNDA7MzdtICAbWzFt3xtbNDdt3NzcG1s0MG2yG1swbd3f39/fIBtbMTszNG3c3BtbMDszNG3c3BtbMzdtINsbWzE7NDdtsLCwG1swbSAbWzFt39/fG1s0N23c3NwbWzQwbd0bWzA7MzRt3xtbMW3cG1swbSAbWzFt3xtbNDdtsrEbWzQwbbEbWzBt3CAbWzM0bdwbWzM3bSDf39vbG1s0NzszMG2wG1s0MDszN22yG1sxbd/fG1swbSAg3tsbWzFt3xtbMDszNG0gG1s0NDszNm2yG1s0MDszN20gG1sxbbAbWzBt29sbWzQ3OzMwbbAbWzE7NDA7MzdtsRtbMG0gG1sxOzM2bd8NChtbMG0bWzZDG1szNm3fG1szN20gIBtbMzZt3BtbMzdtICAbWzM2bdzcG1sxbdwbWzA7MzZt39/bG1sxbbEbWzQ0bbAbWzA7NDQ7MzZtsBtbNDA7MzRtst8bWzM3bd4bWzE7NDc7MzNtIBtbMG3bG1sxOzQ3OzMzbbAbWzQwbd0bWzMwbbAbWzBtsBtbNDc7MzBtshtbNDA7MzdtICAbWzM0bdwbWzM3bSAbWzFt3xtbMG0gG1sxOzM2bbAbWzQ2OzM0bd8bWzQwbdzcG1swbSAbWzFt39/f3NwbWzBtIBtbMzRt3yDc3NwgG1szN20gG1szNG0gG1sxOzM3bdwbWzBt3xtbMW3fG1swbSAbWzE7MzRt3htbMG0g39/fG1sxbd/fDQobWzBtG1sxM0MbWzM0bSAbWzM3bSAgG1szNG2wG1szN20gG1szNG2wG1s0NDszMG2yG1s0MDszN20gIBtbMW3cG1s0N23cG1szM23c27LbG1s0MDszN23b29sbWzMzbd8bWzBtIBtbMzRtICDf3xtbNDQ7MzZtIN/fsBtbNDA7MzRt2xtbMW3f3BtbMG0bWzVD/iAgICDfG1s1QxtbMzRtIP4NChtbMzdtG1syNkMbWzE7MzNt3Nvb3xtbMG0bWzEyQxtbMzRtIN4bWzFt3bAbWzA7NDQ7MzBtshtbNDA7MzdtIBtbMTszMG0gIBtbMzNtYmJzG1swbSBzb2Z0d2FyZQ0KG1syNUMbWzE7MzNt3NsbWzM3bd8bWzBtG1sxMkMbWzM0bdwbWzM3bSAgG1szNG3fDQobWzM3bRtbMjRDG1sxOzM2bdwbWzM3bdsbWzMzbdzcDQobWzBtG1syNUMbWzE7MzNt3N8NChtbMG0bWzI1QxtbMTszM23dDQo=';
+    if (settings && settings.ftelnet_splash) {
+        var f = new File(settings.ftelnet_splash);
+        f.open('rb');
+        var splash = base64_encode(f.read());
+        f.close();
+        return splash || DefaultSplash;
+    } else {
+        return DefaultSplash;
+    }
+}
\ No newline at end of file
diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 215faa48cb..ea3951b097 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -1,2 +1,62 @@
 <!--Home-->
-Things go here.
+<?xjs
+	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
+	load(settings.web_lib + 'ftelnet.js');
+	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
+    
+    var ftelnethelperloaded = false;
+    try {
+      load("ftelnethelper.js");
+      ftelnethelperloaded = true;
+    } catch (e) {
+      // Ignore, we'll display an error below
+    }
+    
+    if (!ftelnethelperloaded) {
+        write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
+        if (user.security.level >= 90) {
+            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b> file from CVS</li></ul>");
+        }
+    } else if (!IsWebSocketToTelnetServiceEnabled()) {
+        write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
+        if (user.security.level >= 90) {
+            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the WebSocket to Telnet Proxy Service<ul><li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WebSocket-Telnet]\r\nPort=1123\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetTelnetPort() + "</pre></li></ul><strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).</p>");
+        }
+    } else {
+?>
+
+<!-- fTelnet --> 
+
+    <style>.fTelnetStatusBar { display : none; }</style>
+	<div id="fTelnetContainer" class="fTelnetContainer"></div>
+    <div class="row">
+        <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
+            <button id="ftelnet-connect" class="btn btn-primary">
+                <?xjs write(_hpl.button_ftelnet); ?>
+            </button>
+        </div>
+    </div>
+    
+	<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
+	<script>
+		var Options = new fTelnetOptions();
+		Options.BareLFtoCRLF = false;
+		Options.BitsPerSecond = 57600;
+		Options.ConnectionType = 'telnet';
+		Options.Emulation = 'ansi-bbs';
+		Options.Enter = '\r';
+		Options.Font = 'CP437';
+		Options.ForceWss = false;
+		Options.Hostname = '<?xjs write(http_request.vhost); ?>';
+		Options.LocalEcho = false;
+		Options.Port = <?xjs write(GetWebSocketToTelnetPort()); ?>;
+		Options.ScreenColumns = 80;
+		Options.ScreenRows = 25;
+        Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+		var fTelnet = new fTelnetClient('fTelnetContainer', Options);
+        $('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+	</script>
+
+<!-- end fTelnet -->
+
+<?xjs } ?>
\ No newline at end of file
diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
new file mode 100644
index 0000000000..a3133fcbb9
--- /dev/null
+++ b/web/root/pages/003-games.xjs
@@ -0,0 +1,138 @@
+<!--Games-->
+<?xjs
+
+	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
+	load(settings.web_lib + 'ftelnet.js');
+
+	if (typeof settings.xtrn_blacklist === 'string') {
+		settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(',');
+	} else {
+		settings.xtrn_blacklist = [];
+	}
+
+	var xtrn = {};
+	xtrn_area.sec_list.forEach(
+		function (sec) {
+			if (!sec.can_access || sec.prog_list.length < 1) return;
+			if (settings.xtrn_blacklist.indexOf(sec.code.toLowerCase()) > -1) {
+				return;
+			}
+			var x = [];
+			sec.prog_list.forEach(
+				function (prog) {
+					if (!prog.can_access || !prog.can_run) return;
+					if (settings.xtrn_blacklist.indexOf(prog.code.toLowerCase()) > -1) {
+						return;
+					}
+					x.push({ c : prog.code, n : prog.name });
+				}
+			);
+			if (x.length > 0) xtrn[sec.name] = x;
+		}
+	);
+
+    var options = null;
+    try {
+      load("ftelnethelper.js");
+      options = load("modopts.js", "logon");
+    } catch (e) {
+      // Ignore, just means modopts.js doesn't exist and so an error will be displayed below
+    }
+
+    if (!options) {
+        write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
+        if (user.security.level >= 90) {
+            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b>, <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/modopts.js'>exec/load/modopts.js</a></b> and <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/ctrl/modopts.ini'>ctrl/modopts.ini</a></b> files from CVS</li></ul></p>");
+        }
+    } else if (!options.rlogin_auto_xtrn) {
+        templatefile = "ftelnet_disabled.inc";
+        if (user.security.level >= 90) {
+            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the rlogin_auto_xtrn feature of the logon module<ul><li>To do this, ensure that the <b>rlogin_auto_xtrn=</b> line in the <b>[logon]</b> section of <b>sbbs/ctrl/modopts.ini</b> is set to <b>true</b></li><li>(Currently, it's set to <b>" + options.rlogin_auto_xtrn + "</b>)</ul></p>");
+        }
+    } else if (!IsWebSocketToRLoginServiceEnabled()) {
+        templatefile = "ftelnet_disabled.inc";
+        if (user.security.level >= 90) {
+            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the WebSocket to RLogin Proxy Service<ul><li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WebSocket-RLogin]\r\nPort=11513\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetRLoginPort() + "</pre></li></ul><strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).</p>");
+        }
+    } else if (!IsRLoginEnabled()) {
+        templatefile = "ftelnet_disabled.inc";
+        if (user.security.level >= 90) {
+            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Allow RLogin connections<ul><li>To do this, ensure that the <b>Options=</b> line in the <b>[BBS]</b> section of <b>sbbs/ctrl/sbbs.ini</b> includes the <b>ALLOW_RLOGIN</b> option.</li><li>Or, if you're running Windows, open the Synchronet Control Panel and click <b>Terminal -> Configure -> RLogin -> Enabled</b> and then click <b>OK</b>.</li></ul></li></ul></p>");
+        }
+    } else {
+?>
+
+<style>.fTelnetStatusBar { display : none; }</style>
+
+<a name="fTelnet"></a>
+<div id="fTelnetContainer" class="fTelnetContainer" style="margin-bottom:1em;"></div>
+
+<div id="xtrn-list" class="list-group">
+	<div id="xtrn-list-template" class="list-group-item" style="display:none;">
+		<h4></h4>
+		<ul class="nav nav-pills">
+			<li id="xtrn-item-template" role="presentation" style="display:none;">
+			<a href="#fTelnet"></a>
+			</li>
+		</ul>
+	</div>
+</div>
+
+<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
+<script type="text/javascript">
+
+	var Options = new fTelnetOptions();
+	Options.BareLFtoCRLF = false;
+	Options.BitsPerSecond = 57600;
+	Options.ConnectionType = 'rlogin';
+	Options.Emulation = 'ansi-bbs';
+	Options.Enter = '\r';
+	Options.Font = 'CP437';
+	Options.ForceWss = false;
+	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
+	Options.LocalEcho = false;
+	Options.Port = <?xjs write(GetWebSocketToRLoginPort()); ?>;
+	Options.RLoginClientUsername = '<?xjs write(user.security.password); ?>';
+	Options.RLoginServerUsername = '<?xjs write(user.alias); ?>';
+	// Options.RLoginTerminalType = 'xtrn=' + http_request.query.code;
+	Options.ScreenColumns = 80;
+	Options.ScreenRows = 25;
+    Options.SplashScreen = Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+	var fTelnet = new fTelnetClient('fTelnetContainer', Options);
+	fTelnet.OnConnectionClose = function () { window.location.reload(); };
+
+	function launchXtrn(code) {
+		$.getJSON(
+			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
+			function(data) { 
+				fTelnet._Options.RLoginTerminalType = 'xtrn=' + code;
+                fTelnet.Connect(); 
+            }
+		);
+	}
+
+	var xtrn = <?xjs write(JSON.stringify(xtrn)); ?>;
+	Object.keys(xtrn).forEach(
+		function (x) {
+			var e = $('#xtrn-list-template').clone();
+			$($(e).find('h4')[0]).text(x);
+			var ul = $($(e).find('ul')[0]);
+			xtrn[x].forEach(
+				function (xx) {
+					var li = $('#xtrn-item-template').clone();
+					var a = $(li).find('a')[0];
+					$(a).text(xx.n);
+					$(a).click(function () { launchXtrn(xx.c); });
+					$(ul).append(li);
+					$(li).show();
+				}
+			);
+			$(e).append(ul);
+			$('#xtrn-list').append(e);
+			$(e).show();
+		}
+	);
+
+</script>
+
+<?xjs } ?>
\ No newline at end of file
-- 
GitLab


From ae4c37e3ff478af125e81ac779bffec7d71e15a4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 00:35:34 -0400
Subject: [PATCH 307/752] Use GetWebSocketServicePort() to determine service
 ports. Untested.

---
 web/root/pages/000-home.xjs | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index ea3951b097..e68cb2d11e 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -3,7 +3,7 @@
 	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
 	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
-    
+
     var ftelnethelperloaded = false;
     try {
       load("ftelnethelper.js");
@@ -11,7 +11,7 @@
     } catch (e) {
       // Ignore, we'll display an error below
     }
-    
+
     if (!ftelnethelperloaded) {
         write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
         if (user.security.level >= 90) {
@@ -25,7 +25,7 @@
     } else {
 ?>
 
-<!-- fTelnet --> 
+<!-- fTelnet -->
 
     <style>.fTelnetStatusBar { display : none; }</style>
 	<div id="fTelnetContainer" class="fTelnetContainer"></div>
@@ -36,9 +36,11 @@
             </button>
         </div>
     </div>
-    
+
 	<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
 	<script>
+        var wsp = <?xjs write(GetWebSocketServicePort()); ?>;
+        var wssp = <?xjs write(GetWebSocketServicePort(true)); ?>;
 		var Options = new fTelnetOptions();
 		Options.BareLFtoCRLF = false;
 		Options.BitsPerSecond = 57600;
@@ -49,7 +51,7 @@
 		Options.ForceWss = false;
 		Options.Hostname = '<?xjs write(http_request.vhost); ?>';
 		Options.LocalEcho = false;
-		Options.Port = <?xjs write(GetWebSocketToTelnetPort()); ?>;
+		Options.Port = location.protocol == 'https:' ? wssp : wsp;
 		Options.ScreenColumns = 80;
 		Options.ScreenRows = 25;
         Options.SplashScreen = '<?xjs write(getSplash()); ?>';
@@ -59,4 +61,4 @@
 
 <!-- end fTelnet -->
 
-<?xjs } ?>
\ No newline at end of file
+<?xjs } ?>
-- 
GitLab


From 4a25752e4f844fbd50a828144e7065f462621579 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 00:52:57 -0400
Subject: [PATCH 308/752] Update sysop instructions to be consistent with
 values ftelnethelper.js looks for in services.ini. This does not yet address
 WSS.

---
 web/root/pages/000-home.xjs | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index e68cb2d11e..1c04984034 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -13,14 +13,34 @@
     }
 
     if (!ftelnethelperloaded) {
-        write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
+?>
+        <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
+<?xjs
         if (user.security.level >= 90) {
-            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b> file from CVS</li></ul>");
+?>
+            <p>
+                Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
+                <ul>
+                    <li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b> file from CVS</li>
+                </ul>
+            </p>
+<?xjs
         }
     } else if (!IsWebSocketToTelnetServiceEnabled()) {
-        write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
+?>
+        <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
+<?xjs
         if (user.security.level >= 90) {
-            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the WebSocket to Telnet Proxy Service<ul><li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WebSocket-Telnet]\r\nPort=1123\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetTelnetPort() + "</pre></li></ul><strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).</p>");
+?>
+            <p>
+                Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
+                <ul>
+                    <li>Enable the WebSocket to Telnet Proxy Service
+                    <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WS]\r\nPort=1123\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetTelnetPort() + "</pre></li>
+                </ul>
+                <strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).
+            </p>
+<?xjs
         }
     } else {
 ?>
-- 
GitLab


From 1cf70fc798339c9a9b4eb38c5af6dc972147ac25 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 01:06:42 -0400
Subject: [PATCH 309/752] Steal WS/WSS instructions from Ree's changes to the
 Runemaster web UI. Formatting may be extensively frigged since I
 de-SSJS-ified it.

---
 web/root/pages/000-home.xjs | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 1c04984034..0b78544053 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -35,8 +35,28 @@
             <p>
                 Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
                 <ul>
-                    <li>Enable the WebSocket to Telnet Proxy Service
-                    <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WS]\r\nPort=1123\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetTelnetPort() + "</pre></li>
+                    <li>Enable the WebSocket to Telnet Proxy Service</li>
+                    <li>Enable the WS and WSS services
+                        <ul>
+                            <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file
+                                <pre>
+                                ;WebSocket service (for fTelnet loaded via http://).
+                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
+                                [WS]
+                                Port=1123
+                                Options=NO_HOST_LOOKUP
+                                Command=websocketservice.js
+                                ;WebSocket Secure service (for fTelnet loaded via https://).
+                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
+                                [WSS]
+                                Port=11235
+                                Options=NO_HOST_LOOKUP | TLS
+                                Command=websocketservice.js
+                                </pre>
+                            </li>
+                        </ul>
+                        <strong>NOTE:</strong> Don't forget to open ports 1123 and 11235 on your firewall and/or add the correct port forwarding rules, if necessary
+                    </li>
                 </ul>
                 <strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).
             </p>
-- 
GitLab


From c260b1116931ffbfe2f3844468ec6d0535aa4460 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 01:17:13 -0400
Subject: [PATCH 310/752] Apply a WebSocketUrlPath option to the ftelnet
 client, where ?Port= the rlogin port, via ftelnethelper, from sbbs.ini.
 Untested. Not sure if this will do what I expect it to. Removed 'template'
 related lines; copy/paste from the stock web UI, I think.

---
 web/root/pages/003-games.xjs | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index a3133fcbb9..2e1d4a8cf0 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -45,17 +45,14 @@
             write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b>, <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/modopts.js'>exec/load/modopts.js</a></b> and <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/ctrl/modopts.ini'>ctrl/modopts.ini</a></b> files from CVS</li></ul></p>");
         }
     } else if (!options.rlogin_auto_xtrn) {
-        templatefile = "ftelnet_disabled.inc";
         if (user.security.level >= 90) {
             write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the rlogin_auto_xtrn feature of the logon module<ul><li>To do this, ensure that the <b>rlogin_auto_xtrn=</b> line in the <b>[logon]</b> section of <b>sbbs/ctrl/modopts.ini</b> is set to <b>true</b></li><li>(Currently, it's set to <b>" + options.rlogin_auto_xtrn + "</b>)</ul></p>");
         }
     } else if (!IsWebSocketToRLoginServiceEnabled()) {
-        templatefile = "ftelnet_disabled.inc";
         if (user.security.level >= 90) {
             write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the WebSocket to RLogin Proxy Service<ul><li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WebSocket-RLogin]\r\nPort=11513\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetRLoginPort() + "</pre></li></ul><strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).</p>");
         }
     } else if (!IsRLoginEnabled()) {
-        templatefile = "ftelnet_disabled.inc";
         if (user.security.level >= 90) {
             write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Allow RLogin connections<ul><li>To do this, ensure that the <b>Options=</b> line in the <b>[BBS]</b> section of <b>sbbs/ctrl/sbbs.ini</b> includes the <b>ALLOW_RLOGIN</b> option.</li><li>Or, if you're running Windows, open the Synchronet Control Panel and click <b>Terminal -> Configure -> RLogin -> Enabled</b> and then click <b>OK</b>.</li></ul></li></ul></p>");
         }
@@ -80,7 +77,8 @@
 
 <script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
 <script type="text/javascript">
-
+    var wsp = <?xjs write(GetWebSocketServicePort()); ?>;
+    var wssp = <?xjs write(GetWebSocketServicePort(true)); ?>;
 	var Options = new fTelnetOptions();
 	Options.BareLFtoCRLF = false;
 	Options.BitsPerSecond = 57600;
@@ -91,22 +89,22 @@
 	Options.ForceWss = false;
 	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
 	Options.LocalEcho = false;
-	Options.Port = <?xjs write(GetWebSocketToRLoginPort()); ?>;
+    Options.Port = location.protocol == 'https:' ? wssp : wsp;
 	Options.RLoginClientUsername = '<?xjs write(user.security.password); ?>';
 	Options.RLoginServerUsername = '<?xjs write(user.alias); ?>';
-	// Options.RLoginTerminalType = 'xtrn=' + http_request.query.code;
 	Options.ScreenColumns = 80;
 	Options.ScreenRows = 25;
     Options.SplashScreen = Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+    Options.WebSocketUrlPath = '?Port=<?xjs write(GetRLoginPort()); ?>';
 	var fTelnet = new fTelnetClient('fTelnetContainer', Options);
 	fTelnet.OnConnectionClose = function () { window.location.reload(); };
 
 	function launchXtrn(code) {
 		$.getJSON(
 			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function(data) { 
+			function(data) {
 				fTelnet._Options.RLoginTerminalType = 'xtrn=' + code;
-                fTelnet.Connect(); 
+                fTelnet.Connect();
             }
 		);
 	}
@@ -135,4 +133,4 @@
 
 </script>
 
-<?xjs } ?>
\ No newline at end of file
+<?xjs } ?>
-- 
GitLab


From f3f4529ff0881202d20b8d9d6c5c88d481bb1352 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 01:22:13 -0400
Subject: [PATCH 311/752] IsWebSocketServiceEnabled (not Telnet-specific now.)

---
 web/root/pages/000-home.xjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/pages/000-home.xjs b/web/root/pages/000-home.xjs
index 0b78544053..9c48d5b8f1 100644
--- a/web/root/pages/000-home.xjs
+++ b/web/root/pages/000-home.xjs
@@ -26,7 +26,7 @@
             </p>
 <?xjs
         }
-    } else if (!IsWebSocketToTelnetServiceEnabled()) {
+    } else if (!IsWebSocketServiceEnabled()) {
 ?>
         <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
 <?xjs
@@ -35,7 +35,7 @@
             <p>
                 Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
                 <ul>
-                    <li>Enable the WebSocket to Telnet Proxy Service</li>
+                    <li>Enable the WebSocket Proxy Service</li>
                     <li>Enable the WS and WSS services
                         <ul>
                             <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file
-- 
GitLab


From 57539a9cfacb0ec64dd8ca283cad7db12499e47f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 01:27:35 -0400
Subject: [PATCH 312/752] De-SSJS-ify the instructions, possibly frigging up
 formatting in the process. Use IsWebSocketServiceEnabled() (not
 RLogin-specific function) Paste in WS/WSS setup instructions from
 000-Home.xjs.

---
 web/root/pages/003-games.xjs | 50 +++++++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 9 deletions(-)

diff --git a/web/root/pages/003-games.xjs b/web/root/pages/003-games.xjs
index 2e1d4a8cf0..cf2f8acb2f 100644
--- a/web/root/pages/003-games.xjs
+++ b/web/root/pages/003-games.xjs
@@ -40,24 +40,56 @@
     }
 
     if (!options) {
-        write("<p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>");
-        if (user.security.level >= 90) {
-            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b>, <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/modopts.js'>exec/load/modopts.js</a></b> and <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/ctrl/modopts.ini'>ctrl/modopts.ini</a></b> files from CVS</li></ul></p>");
+?>
+        <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
+<?xjs if (user.security.level >= 90) { ?>
+        <p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b>, <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/modopts.js'>exec/load/modopts.js</a></b> and <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/ctrl/modopts.ini'>ctrl/modopts.ini</a></b> files from CVS</li></ul></p>
+<?xjs
         }
     } else if (!options.rlogin_auto_xtrn) {
         if (user.security.level >= 90) {
-            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the rlogin_auto_xtrn feature of the logon module<ul><li>To do this, ensure that the <b>rlogin_auto_xtrn=</b> line in the <b>[logon]</b> section of <b>sbbs/ctrl/modopts.ini</b> is set to <b>true</b></li><li>(Currently, it's set to <b>" + options.rlogin_auto_xtrn + "</b>)</ul></p>");
+?>
+            <p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the rlogin_auto_xtrn feature of the logon module<ul><li>To do this, ensure that the <b>rlogin_auto_xtrn=</b> line in the <b>[logon]</b> section of <b>sbbs/ctrl/modopts.ini</b> is set to <b>true</b></li><li>(Currently, it's set to <b>" + options.rlogin_auto_xtrn + "</b>)</ul></p>
+<?xjs
         }
-    } else if (!IsWebSocketToRLoginServiceEnabled()) {
+    } else if (!IsWebSocketServiceEnabled()) {
         if (user.security.level >= 90) {
-            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the WebSocket to RLogin Proxy Service<ul><li>To do this, add this block to your <b>sbbs/ctrl/services.ini file<pre>[WebSocket-RLogin]\r\nPort=11513\r\nOptions=NO_HOST_LOOKUP\r\nCommand=websocketservice.js localhost " + GetRLoginPort() + "</pre></li></ul><strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).</p>");
+?>
+            <p>
+                Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
+                <ul>
+                    <li>Enable the WebSocket Proxy Service</li>
+                    <li>Enable the WS and WSS services
+                        <ul>
+                            <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file
+                                <pre>
+                                ;WebSocket service (for fTelnet loaded via http://).
+                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
+                                [WS]
+                                Port=1123
+                                Options=NO_HOST_LOOKUP
+                                Command=websocketservice.js
+                                ;WebSocket Secure service (for fTelnet loaded via https://).
+                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
+                                [WSS]
+                                Port=11235
+                                Options=NO_HOST_LOOKUP | TLS
+                                Command=websocketservice.js
+                                </pre>
+                            </li>
+                        </ul>
+                        <strong>NOTE:</strong> Don't forget to open ports 1123 and 11235 on your firewall and/or add the correct port forwarding rules, if necessary
+                    </li>
+                </ul>
+                <strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).
+            </p>
+<?xjs
         }
     } else if (!IsRLoginEnabled()) {
         if (user.security.level >= 90) {
-            write("<p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Allow RLogin connections<ul><li>To do this, ensure that the <b>Options=</b> line in the <b>[BBS]</b> section of <b>sbbs/ctrl/sbbs.ini</b> includes the <b>ALLOW_RLOGIN</b> option.</li><li>Or, if you're running Windows, open the Synchronet Control Panel and click <b>Terminal -> Configure -> RLogin -> Enabled</b> and then click <b>OK</b>.</li></ul></li></ul></p>");
-        }
-    } else {
 ?>
+        <p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Allow RLogin connections<ul><li>To do this, ensure that the <b>Options=</b> line in the <b>[BBS]</b> section of <b>sbbs/ctrl/sbbs.ini</b> includes the <b>ALLOW_RLOGIN</b> option.</li><li>Or, if you're running Windows, open the Synchronet Control Panel and click <b>Terminal -> Configure -> RLogin -> Enabled</b> and then click <b>OK</b>.</li></ul></li></ul></p>
+<?xjs } } else { ?>
 
 <style>.fTelnetStatusBar { display : none; }</style>
 
-- 
GitLab


From c03a17c9e9eef1900d37ee15c32a07fef08b4cdf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 03:01:05 -0400
Subject: [PATCH 313/752] An install script. Barely tested, and bare minimum
 for now. To-do: - prompt for guest username, with default - prompt for
 ftelnet enable/disable (and implement enable/disable) - prompt for ftelnet
 splash .ans if enabled - add WS/WSS services if missing, and if ftelnet
 enabled - nuke old websocket telnet/rlogin proxies if configured - copy
 web/pages/examples/* to web/pages/ (once pages has been moved out of docroot)
 - likewise for web/sidebar/examples/* - test for download/extract/ini-update
 success before proceeding to next step - provide URL/info re: further config
 instructions upon exit

---
 mods/webv4-installer.js | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 mods/webv4-installer.js

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
new file mode 100644
index 0000000000..7535a229c3
--- /dev/null
+++ b/mods/webv4-installer.js
@@ -0,0 +1,63 @@
+load('http.js');
+
+function download(url, target) {
+	const http = new HTTPRequest();
+	const zip = http.Get(url);
+	const f = new File(target);
+	f.open('wb');
+	f.write(zip);
+	f.close();
+}
+
+function extract(file, target) {
+	if (!file_exists(install_dir)) mkdir(install_dir);
+	system.exec(system.exec_dir + 'unzip -u ' + download_target + ' -d ' + install_dir);
+}
+
+function update_sbbs_ini(root_directory, error_directory) {
+	file_backup(system.ctrl_dir + 'sbbs.ini');
+	const f = new File(system.ctrl_dir + 'sbbs.ini');
+	f.open('r+');
+	f.iniSetValue('Web', 'RootDirectory', root_directory);
+	f.iniSetValue('Web', 'ErrorDirectory', error_directory);
+	f.close();
+}
+
+function update_modopts_ini(modopts) {
+	file_backup(system.ctrl_dir + 'modopts.ini');
+	const f = new File(system.ctrl_dir + 'modopts.ini');
+	f.open('r+');
+	f.iniSetObject('web', modopts);
+	f.close();
+}
+
+const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/master';
+const download_target = system.temp_dir + 'webv4.zip';
+const extract_dir = fullpath(system.exec_dir + '../web');
+const install_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
+const root_directory = fullpath(system.exec_dir + '../web/synchronet-web-v4-master/root');
+const error_directory = fullpath(root_directory + '/errors');
+const modopts_web = {
+	guest : 'Guest',
+	timeout : 43200,
+	inactivity : 900,
+	user_registration : true,
+	minimum_password_length : 6,
+	maximum_telegram_length : 800,
+	web_directory : install_dir,
+	ftelnet_splash : '../text/synch.ans',
+	keyboard_navigation : false,
+	vote_functions : true,
+	refresh_interval : 60000,
+	xtrn_blacklist : 'scfg,oneliner',
+	layout_sidebar_off : false,
+	layout_sidebar_left : false,
+	layout_full_width : false,
+	forum_extended_ascii : false,
+	max_messages : 0
+};
+
+download(zip_url, download_target);
+extract(download_target, extract_dir);
+update_sbbs_ini(root_directory, error_directory);
+update_modopts_ini(modopts_web);
-- 
GitLab


From 1623eda431800d3d2aa3bb7a66c30ff8171cc352 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 04:03:11 -0400
Subject: [PATCH 314/752] Preserve existing settings from modopts, if any. Some
 error handling. Some prompting for settings.

---
 mods/webv4-installer.js | 125 ++++++++++++++++++++++++++++------------
 1 file changed, 89 insertions(+), 36 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 7535a229c3..4ad1d69d45 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -2,33 +2,62 @@ load('http.js');
 
 function download(url, target) {
 	const http = new HTTPRequest();
-	const zip = http.Get(url);
+    try {
+	    const zip = http.Get(url);
+    } catch (err) {
+        log(err);
+        return false;
+    }
 	const f = new File(target);
-	f.open('wb');
-	f.write(zip);
+	if (!f.open('wb')) return false;
+	if (!f.write(zip)) return false;
 	f.close();
+    return true;
 }
 
 function extract(file, target) {
-	if (!file_exists(install_dir)) mkdir(install_dir);
-	system.exec(system.exec_dir + 'unzip -u ' + download_target + ' -d ' + install_dir);
+	if (!file_isdir(install_dir)) {
+        if (!mkdir(install_dir)) return false;
+    }
+	return system.exec(system.exec_dir + 'unzip -uqo ' + file + ' -d ' + target) == 0;
 }
 
 function update_sbbs_ini(root_directory, error_directory) {
-	file_backup(system.ctrl_dir + 'sbbs.ini');
+	if (!file_backup(system.ctrl_dir + 'sbbs.ini')) return false;
 	const f = new File(system.ctrl_dir + 'sbbs.ini');
-	f.open('r+');
-	f.iniSetValue('Web', 'RootDirectory', root_directory);
-	f.iniSetValue('Web', 'ErrorDirectory', error_directory);
+	if (!f.open('r+')) return false;
+	if (!f.iniSetValue('Web', 'RootDirectory', root_directory)) return false;
+	if (!f.iniSetValue('Web', 'ErrorDirectory', error_directory)) return false;
 	f.close();
+    return true;
 }
 
 function update_modopts_ini(modopts) {
-	file_backup(system.ctrl_dir + 'modopts.ini');
+	if (!file_backup(system.ctrl_dir + 'modopts.ini')) return false;
 	const f = new File(system.ctrl_dir + 'modopts.ini');
-	f.open('r+');
-	f.iniSetObject('web', modopts);
+	if (!f.open('r+')) return false;
+	if (!f.iniSetObject('web', modopts)) return false;
 	f.close();
+    return true;
+}
+
+function get_modopts_ini() {
+    const f = new File(system.ctrl_dir + 'modopts.ini');
+    if (!f.open('r')) return false;
+    const ini = f.iniGetObject('web');
+    f.close();
+    return ini;
+}
+
+function get_settings(modopts) {
+    writeln('---');
+    var i;
+    i = prompt('Guest user alias [' + modopts.guest + ']');
+    if (i != '') modopts.guest = i;
+    i = prompt('Path to ftelnet background .ans [' + modopts.ftelnet_splash + ']');
+    if (i != '') modopts.splash = i;
+    writeln('---');
+    return modopts;
 }
 
 const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/master';
@@ -37,27 +66,51 @@ const extract_dir = fullpath(system.exec_dir + '../web');
 const install_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
 const root_directory = fullpath(system.exec_dir + '../web/synchronet-web-v4-master/root');
 const error_directory = fullpath(root_directory + '/errors');
-const modopts_web = {
-	guest : 'Guest',
-	timeout : 43200,
-	inactivity : 900,
-	user_registration : true,
-	minimum_password_length : 6,
-	maximum_telegram_length : 800,
-	web_directory : install_dir,
-	ftelnet_splash : '../text/synch.ans',
-	keyboard_navigation : false,
-	vote_functions : true,
-	refresh_interval : 60000,
-	xtrn_blacklist : 'scfg,oneliner',
-	layout_sidebar_off : false,
-	layout_sidebar_left : false,
-	layout_full_width : false,
-	forum_extended_ascii : false,
-	max_messages : 0
-};
-
-download(zip_url, download_target);
-extract(download_target, extract_dir);
-update_sbbs_ini(root_directory, error_directory);
-update_modopts_ini(modopts_web);
+var modopts_web = get_modopts_ini();
+if (!modopts_web) {
+    modopts_web = {
+    	guest : 'Guest',
+    	timeout : 43200,
+    	inactivity : 900,
+    	user_registration : true,
+    	minimum_password_length : 6,
+    	maximum_telegram_length : 800,
+    	web_directory : install_dir,
+    	ftelnet_splash : '../text/synch.ans',
+    	keyboard_navigation : false,
+    	vote_functions : true,
+    	refresh_interval : 60000,
+    	xtrn_blacklist : 'scfg,oneliner',
+    	layout_sidebar_off : false,
+    	layout_sidebar_left : false,
+    	layout_full_width : false,
+    	forum_extended_ascii : false,
+    	max_messages : 0
+    };
+}
+
+writeln('Downloading ' + zip_url + ' to ' + download_target + ' ...');
+if (!download(zip_url, download_target)) {
+    writeln('Download of ' + zip_url + ' failed. Exiting.');
+    exit();
+}
+
+writeln('Extracting ' + download_target + ' to ' + extract_dir + ' ...');
+if (!extract(download_target, extract_dir)) {
+    writeln('Extraction of ' + download_target + ' failed. Exiting.');
+    exit();
+}
+
+writeln('Updating sbbs.ini ...');
+if (!update_sbbs_ini(root_directory, error_directory)) {
+    writeln('Failed to update sbbs.ini. Exiting.');
+    exit();
+}
+
+modopts_web = get_settings(modopts_web);
+
+writeln('Updating modopts.ini ...');
+if (!update_modopts_ini(modopts_web)) {
+    writeln('Failed to update modopts.ini. Exiting.');
+    exit();
+}
-- 
GitLab


From 1e36592ee2bf58b7940bf77d1d612ad411a75ce7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 04:21:41 -0400
Subject: [PATCH 315/752] Simplify the getting of settings.

---
 mods/webv4-installer.js | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 4ad1d69d45..5f5daaa874 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -49,14 +49,15 @@ function get_modopts_ini() {
     return ini;
 }
 
+function get_setting(text, value) {
+    const i = prompt(text + ' [' + value + ']');
+    return (i == '' ? value : i);
+}
+
 function get_settings(modopts) {
     writeln('---');
-    var i;
-    i = prompt('Guest user alias [' + modopts.guest + ']');
-    if (i != '') modopts.guest = i;
-    i = prompt('Path to ftelnet background .ans [' + modopts.ftelnet_splash + ']');
-    if (i != '') modopts.splash = i;
-    writeln('---');
+    modopts.guest = get_setting('Guest user alias', modopts.guest);
+    modopts.ftelnet_splash = get_setting('Path to ftelnet background .ans', modopts.ftelnet_splash);
     return modopts;
 }
 
-- 
GitLab


From e4ce9ca4fdfdc010001ccb32c9884faf92c59f78 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 14:01:23 -0400
Subject: [PATCH 316/752] Move pages/ out of the document root. These are only
 served via the layout script. Moved stock pages into pages/.examples
 directory so that sysops can make customizations that won't conflict with
 what's in the repository.

---
 web/{root/pages => pages/.examples}/000-home.xjs          | 0
 web/{root/pages => pages/.examples}/000-mail.xjs          | 0
 web/{root/pages => pages/.examples}/000-register.xjs      | 0
 web/{root/pages => pages/.examples}/001-forum.ssjs        | 0
 web/{root/pages => pages/.examples}/002-files.xjs         | 0
 web/{root/pages => pages/.examples}/003-games.xjs         | 0
 web/{root/pages => pages/.examples}/More/001-userlist.xjs | 0
 web/{root/pages => pages/.examples}/More/999-sbbslist.xjs | 0
 web/{root/pages => pages/.examples}/More/webctrl.ini      | 0
 web/{root => }/pages/webctrl.ini                          | 0
 10 files changed, 0 insertions(+), 0 deletions(-)
 rename web/{root/pages => pages/.examples}/000-home.xjs (100%)
 rename web/{root/pages => pages/.examples}/000-mail.xjs (100%)
 rename web/{root/pages => pages/.examples}/000-register.xjs (100%)
 rename web/{root/pages => pages/.examples}/001-forum.ssjs (100%)
 rename web/{root/pages => pages/.examples}/002-files.xjs (100%)
 rename web/{root/pages => pages/.examples}/003-games.xjs (100%)
 rename web/{root/pages => pages/.examples}/More/001-userlist.xjs (100%)
 rename web/{root/pages => pages/.examples}/More/999-sbbslist.xjs (100%)
 rename web/{root/pages => pages/.examples}/More/webctrl.ini (100%)
 rename web/{root => }/pages/webctrl.ini (100%)

diff --git a/web/root/pages/000-home.xjs b/web/pages/.examples/000-home.xjs
similarity index 100%
rename from web/root/pages/000-home.xjs
rename to web/pages/.examples/000-home.xjs
diff --git a/web/root/pages/000-mail.xjs b/web/pages/.examples/000-mail.xjs
similarity index 100%
rename from web/root/pages/000-mail.xjs
rename to web/pages/.examples/000-mail.xjs
diff --git a/web/root/pages/000-register.xjs b/web/pages/.examples/000-register.xjs
similarity index 100%
rename from web/root/pages/000-register.xjs
rename to web/pages/.examples/000-register.xjs
diff --git a/web/root/pages/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
similarity index 100%
rename from web/root/pages/001-forum.ssjs
rename to web/pages/.examples/001-forum.ssjs
diff --git a/web/root/pages/002-files.xjs b/web/pages/.examples/002-files.xjs
similarity index 100%
rename from web/root/pages/002-files.xjs
rename to web/pages/.examples/002-files.xjs
diff --git a/web/root/pages/003-games.xjs b/web/pages/.examples/003-games.xjs
similarity index 100%
rename from web/root/pages/003-games.xjs
rename to web/pages/.examples/003-games.xjs
diff --git a/web/root/pages/More/001-userlist.xjs b/web/pages/.examples/More/001-userlist.xjs
similarity index 100%
rename from web/root/pages/More/001-userlist.xjs
rename to web/pages/.examples/More/001-userlist.xjs
diff --git a/web/root/pages/More/999-sbbslist.xjs b/web/pages/.examples/More/999-sbbslist.xjs
similarity index 100%
rename from web/root/pages/More/999-sbbslist.xjs
rename to web/pages/.examples/More/999-sbbslist.xjs
diff --git a/web/root/pages/More/webctrl.ini b/web/pages/.examples/More/webctrl.ini
similarity index 100%
rename from web/root/pages/More/webctrl.ini
rename to web/pages/.examples/More/webctrl.ini
diff --git a/web/root/pages/webctrl.ini b/web/pages/webctrl.ini
similarity index 100%
rename from web/root/pages/webctrl.ini
rename to web/pages/webctrl.ini
-- 
GitLab


From 10f8aea1b93adedd345a70df311e059a6ad4e370 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 14:07:37 -0400
Subject: [PATCH 317/752] New pages/ location.

---
 web/lib/pages.js                          | 13 +++++--------
 web/pages/.examples/000-home.xjs          |  1 -
 web/pages/.examples/000-mail.xjs          |  7 +++----
 web/pages/.examples/001-forum.ssjs        |  1 -
 web/pages/.examples/002-files.xjs         | 11 +++++------
 web/pages/.examples/003-games.xjs         |  1 -
 web/pages/.examples/More/001-userlist.xjs |  9 ++++-----
 web/root/index.xjs                        | 19 +++++++++----------
 8 files changed, 26 insertions(+), 36 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 53c9299695..fa6cb98d1d 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -27,9 +27,7 @@ function webCtrlTest(ini, filename) {
 
 function getCtrlLine(file) {
 
-	if (fullpath(file).indexOf(fullpath(settings.web_root + '/pages')) !== 0) {
-		return;
-	}
+	if (fullpath(file).indexOf(fullpath(settings.web_pages)) !== 0) return;
 
 	var f = new File(file);
 	var ctrl = '';
@@ -80,9 +78,7 @@ function getCtrlLine(file) {
 function getPageList(dir) {
 
 	dir = backslash(fullpath(dir));
-	if (dir.indexOf(backslash(fullpath(settings.web_root + '/pages'))) !== 0) {
-		return {};
-	}
+	if (dir.indexOf(settings.web_pages) !== 0) return {};
 
 	var webctrl = getWebCtrl(dir);
 
@@ -91,6 +87,7 @@ function getPageList(dir) {
 		function (e) {
 			if (file_isdir(e)) {
 				e = fullpath(e);
+                if (e.search(/\.examples.$/) > -1) return;
 				var list = getPageList(e);
 				if (Object.keys(list).length > 0) {
 					pages[e.split(e.substr(-1, 1)).slice(-2, -1)] = list;
@@ -116,7 +113,7 @@ function getPage(page) {
 
 	var ret = '';
 
-	page = settings.web_root + 'pages/' + page;
+	page = settings.web_pages + page;
 
 	if (!file_exists(page)) return ret;
 
@@ -159,4 +156,4 @@ function getPage(page) {
 
 	return ret;
 
-}
\ No newline at end of file
+}
diff --git a/web/pages/.examples/000-home.xjs b/web/pages/.examples/000-home.xjs
index 9c48d5b8f1..ca0b275104 100644
--- a/web/pages/.examples/000-home.xjs
+++ b/web/pages/.examples/000-home.xjs
@@ -1,6 +1,5 @@
 <!--Home-->
 <?xjs
-	if (typeof argv[0] != 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
 	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
 
diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index 1e3710cae8..dd7dfe4481 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -2,7 +2,6 @@
 
 <?xjs
 
-if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 if (user.number == 0 || user.alias == settings.guest) exit();
 
 load('sbbsdefs.js');
@@ -30,12 +29,12 @@ function sent() {
 				</div>
 			</div>
 			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
-				<?xjs write(!sent() ? _mpl.label_message_from : _mpl.label_message_to); ?>: 
-				<strong><?xjs write(!sent() ? header.from : header.to); ?></strong> 
+				<?xjs write(!sent() ? _mpl.label_message_from : _mpl.label_message_to); ?>:
+				<strong><?xjs write(!sent() ? header.from : header.to); ?></strong>
 				<?xjs write(_mpl.label_message_date); ?>
 				<?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
 				<p>
-					<?xjs write(_mpl.label_message_subject); ?>: 
+					<?xjs write(_mpl.label_message_subject); ?>:
 					<strong><?xjs write(header.subject); ?></strong>
 				</p>
 			</div>
diff --git a/web/pages/.examples/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
index 7296374d5c..b45107a4fe 100644
--- a/web/pages/.examples/001-forum.ssjs
+++ b/web/pages/.examples/001-forum.ssjs
@@ -1,5 +1,4 @@
 //Forum
-if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 
 load('sbbsdefs.js');
 load(system.exec_dir + '../web/lib/init.js');
diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index 21c7862f64..99c49b33c9 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -1,7 +1,6 @@
 <!--Files-->
 
 <?xjs
-	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 	load(system.exec_dir + '../web/lib/init.js');
 	load(settings.web_lib + 'files.js');
 
@@ -73,8 +72,8 @@
 		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;dir=<?xjs write(dir.dir.code); ?>" class="list-group-item striped">
 			<h4><strong><?xjs write(dir.dir.name); ?></strong></h4>
 			<p>
-				<?xjs write(dir.dir.description); ?>: 
-				<?xjs write(dir.fileCount); ?> 
+				<?xjs write(dir.dir.description); ?>:
+				<?xjs write(dir.fileCount); ?>
 				<?xjs write(dir.fileCount === 1 ? _fpl.stat_suffix_file : _fpl.stat_suffix_files); ?>
 			</p>
 		</a>
@@ -98,8 +97,8 @@
 		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(library.index); ?>" class="list-group-item striped">
 			<h3><strong><?xjs write(library.name); ?></strong></h3>
 			<p>
-				<?xjs write(library.description); ?>: 
-				<?xjs write(library.dir_list.length); ?> 
+				<?xjs write(library.description); ?>:
+				<?xjs write(library.dir_list.length); ?>
 				<?xjs write(library.dir_list.length === 1 ? _fpl.stat_suffix_directory : _fpl.stat_suffix_directories); ?>
 			</p>
 		</a>
@@ -109,4 +108,4 @@
 		<?xjs listLibraries().forEach(writeLibrary); ?>
 	</div>
 
-<?xjs } ?>
\ No newline at end of file
+<?xjs } ?>
diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index cf2f8acb2f..6935ed2879 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -1,7 +1,6 @@
 <!--Games-->
 <?xjs
 
-	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 	load(settings.web_lib + 'ftelnet.js');
 
 	if (typeof settings.xtrn_blacklist === 'string') {
diff --git a/web/pages/.examples/More/001-userlist.xjs b/web/pages/.examples/More/001-userlist.xjs
index 63358a3bfc..e3645277c6 100644
--- a/web/pages/.examples/More/001-userlist.xjs
+++ b/web/pages/.examples/More/001-userlist.xjs
@@ -1,6 +1,5 @@
 <!--User List-->
 <?xjs
-	if (typeof argv[0] !== 'boolean' || !argv[0]) exit();
 
 	var pageSize = 500;
 
@@ -37,7 +36,7 @@
 		http_request.host,
 		http_request.request_string.split("&")[0]
 	);
-	
+
 	function sortUser(a, b, sortOrder, type) {
 		if (type === 'string') {
 			a = a.toUpperCase();
@@ -81,7 +80,7 @@
 		}
 		return ret;
 	}
-	
+
 	function makeSortURLs(field, order) {
 		return format(
 			'<a class="icon" ' +
@@ -121,7 +120,7 @@
 		}
 		return ret;
 	}
-	
+
 	function copyProperties(source, dest) {
 		for (var property in source) {
 			if ((	typeof source[property] === 'string' ||
@@ -198,4 +197,4 @@
 	<ul class="pager">
 		<?xjs writeln(pager.previous); writeln(pager.next); ?>
 	</ul>
-</nav>
\ No newline at end of file
+</nav>
diff --git a/web/root/index.xjs b/web/root/index.xjs
index e11f360917..afc2ceceda 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -13,18 +13,18 @@
 		(	typeof http_request.query.page === 'undefined' ||
 			!file_exists(
 				fullpath(
-					settings.web_root + 'pages/' + http_request.query.page[0]
+					settings.web_pages + http_request.query.page[0]
 				)
 			) ||
 			fullpath(
-				settings.web_root + 'pages/' + http_request.query.page[0]
-			).indexOf(fullpath(settings.web_root + 'pages')) !== 0
+				settings.web_pages + http_request.query.page[0]
+			).indexOf(fullpath(settings.web_pages)) !== 0
 		)
 		? '000-home.xjs'
 		: http_request.query.page[0]
 	);
 
-	var page_ctrl = getCtrlLine(settings.web_root + 'pages/' + page);
+	var page_ctrl = getCtrlLine(settings.web_pages + page);
 
 	function writePage() {
 		var ini = getWebCtrl(
@@ -141,7 +141,7 @@
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
 					<ul class="nav navbar-nav">
-						<?xjs _menu(getPageList(settings.web_root + 'pages/')); ?>
+						<?xjs _menu(getPageList(settings.web_pages)); ?>
 					</ul>
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
@@ -202,12 +202,11 @@
 			<div class="row row-offcanvas row-offcanvas-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?>">
 				<?xjs if (settings.layout_sidebar_left) _sidebar(); ?>
 				<div class="col-xs-12 col-sm-<?xjs write(settings.layout_sidebar_off || page_ctrl.options.no_sidebar ? 12 : 9); ?>">
+					<div style="clear:both;">
 					<p class="pull-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?> visible-xs">
-						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas">
-							<span class="glyphicon glyphicon-tasks"></span>
-							<?xjs write(_language.label_sidebar); ?>
-						</button>
+						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><span class="glyphicon glyphicon-tasks"></span><?xjs write(_language.label_sidebar); ?></button>
 					</p>
+					</div>
 					<?xjs writePage(); ?>
 				</div>
 				<?xjs if (!settings.layout_sidebar_left || settings.layout_sidebar_right) _sidebar(); ?>
@@ -237,4 +236,4 @@
 
 	</body>
 
-</html>
\ No newline at end of file
+</html>
-- 
GitLab


From 2532e478f88704ac8af39e01419a96200a851838 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 14:12:22 -0400
Subject: [PATCH 318/752] Moved sidebar/ out of document root.

---
 web/lib/sidebar.js                         | 2 +-
 web/{root => }/sidebar/001-nodelist.xjs    | 0
 web/{root => }/sidebar/003-systemStats.xjs | 0
 3 files changed, 1 insertion(+), 1 deletion(-)
 rename web/{root => }/sidebar/001-nodelist.xjs (100%)
 rename web/{root => }/sidebar/003-systemStats.xjs (100%)

diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index 1042cc23c2..0135dba83f 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -9,7 +9,7 @@ function getFileContents(file) {
 }
 
 function getSidebarModules() {
-	return directory(settings.web_root + 'sidebar/*').filter(
+	return directory(settings.web_sidebar + '*').filter(
 		function (e, i, a) { return (!file_isdir(e)); }
 	);
 }
diff --git a/web/root/sidebar/001-nodelist.xjs b/web/sidebar/001-nodelist.xjs
similarity index 100%
rename from web/root/sidebar/001-nodelist.xjs
rename to web/sidebar/001-nodelist.xjs
diff --git a/web/root/sidebar/003-systemStats.xjs b/web/sidebar/003-systemStats.xjs
similarity index 100%
rename from web/root/sidebar/003-systemStats.xjs
rename to web/sidebar/003-systemStats.xjs
-- 
GitLab


From 8c440e2dbc6393d215bd16a8ef14dc112ba30561 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 14:15:27 -0400
Subject: [PATCH 319/752] Moved stock sidebar modules to .examples
 subdirectory.

---
 web/sidebar/{ => .examples}/001-nodelist.xjs    | 0
 web/sidebar/{ => .examples}/003-systemStats.xjs | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename web/sidebar/{ => .examples}/001-nodelist.xjs (100%)
 rename web/sidebar/{ => .examples}/003-systemStats.xjs (100%)

diff --git a/web/sidebar/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
similarity index 100%
rename from web/sidebar/001-nodelist.xjs
rename to web/sidebar/.examples/001-nodelist.xjs
diff --git a/web/sidebar/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
similarity index 100%
rename from web/sidebar/003-systemStats.xjs
rename to web/sidebar/.examples/003-systemStats.xjs
-- 
GitLab


From 4e63f244d1172afa7473518bc30eed54a2dd74f7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 16:21:17 -0400
Subject: [PATCH 320/752] Moved webctrl.ini to examples directory.

---
 web/pages/{ => .examples}/webctrl.ini | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename web/pages/{ => .examples}/webctrl.ini (100%)

diff --git a/web/pages/webctrl.ini b/web/pages/.examples/webctrl.ini
similarity index 100%
rename from web/pages/webctrl.ini
rename to web/pages/.examples/webctrl.ini
-- 
GitLab


From dae32f7515183cff64d23cbed563a5bb6635d44b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 16:51:10 -0400
Subject: [PATCH 321/752] Extract zip to temp dir, move files around, clean up
 after yourself, don't clobber custom pages or sidebar modules. More prompts,
 including an 'enable ftelnet' option that isn't actually used for anything
 yet. Update sbbs.ini RootDirectory and ErrorDirectory last of all; should
 trigger recycle. Install path is system.exec_dir + ../webv4. Rather than move
 stock components out of the way, we'll just make a new space.  People who
 installed to, say, system.exec_dir + ../web will need to migrate content;
 instructions forthcoming.

---
 mods/webv4-installer.js | 109 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 97 insertions(+), 12 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 5f5daaa874..dd9f7da1b2 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -54,19 +54,71 @@ function get_setting(text, value) {
     return (i == '' ? value : i);
 }
 
+function confirm_setting(text, value) {
+    if (!value) {
+        return !deny(text + ' [' + value + ']');
+    } else {
+        return confirm(text + ' [' + value + ']');
+    }
+}
+
 function get_settings(modopts) {
-    writeln('---');
+    write('\r\n---\r\n\r\n');
+    write('Configuration - press enter to accept default/current value.\r\n\r\n');
     modopts.guest = get_setting('Guest user alias', modopts.guest);
-    modopts.ftelnet_splash = get_setting('Path to ftelnet background .ans', modopts.ftelnet_splash);
+    modopts.ftelnet = confirm_setting('Enable fTelnet', modopts.ftelnet);
+    if (modopts.ftelnet) modopts.ftelnet_splash = get_setting('Path to ftelnet background .ans', modopts.ftelnet_splash);
+    modopts.user_registration = confirm_setting('Allow new user registration via the web', modopts.user_registration);
+    write('\r\n\r\n---\r\n\r\n');
     return modopts;
 }
 
+function copy_dir_contents(src, dest, overwrite) {
+    src = backslash(fullpath(src));
+    dest = backslash(fullpath(dest));
+    const delim = src.substr(-1);
+    if (!file_isdir(dest)) mkdir(dest);
+    directory(src + '*').forEach(
+    	function (e) {
+    		e = fullpath(e);
+    		if (file_isdir(e)) {
+    			const path = e.split(delim);
+    			var df = dest + path[path.length - 2];
+    			if (!file_isdir(df)) mkdir(df);
+    			copy_dir_contents(e, df, overwrite);
+    		} else {
+    			var df = dest + file_getname(e);
+    			if (!file_exists(df) || overwrite) {
+					file_copy(e, dest + file_getname(e));
+    			}
+    		}
+    	}
+    );
+}
+
+// yikes
+function remove_dir(dir) {
+    dir = backslash(fullpath(dir));
+    directory(dir + '*').forEach(
+    	function (e) {
+    		if (file_isdir(e)) {
+    			remove_dir(e);
+    		} else {
+	    		file_remove(e);
+	    	}
+    	}
+    );
+    rmdir(dir);
+}
+
 const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/master';
 const download_target = system.temp_dir + 'webv4.zip';
-const extract_dir = fullpath(system.exec_dir + '../web');
-const install_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
-const root_directory = fullpath(system.exec_dir + '../web/synchronet-web-v4-master/root');
+const extract_dir = fullpath(system.temp_dir);
+const temp_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
+const install_dir = fullpath(system.exec_dir + '../webv4');
+const root_directory = fullpath(install_dir + '/root');
 const error_directory = fullpath(root_directory + '/errors');
+
 var modopts_web = get_modopts_ini();
 if (!modopts_web) {
     modopts_web = {
@@ -77,6 +129,7 @@ if (!modopts_web) {
     	minimum_password_length : 6,
     	maximum_telegram_length : 800,
     	web_directory : install_dir,
+        ftelnet : true,
     	ftelnet_splash : '../text/synch.ans',
     	keyboard_navigation : false,
     	vote_functions : true,
@@ -90,23 +143,42 @@ if (!modopts_web) {
     };
 }
 
-writeln('Downloading ' + zip_url + ' to ' + download_target + ' ...');
+write('\r\n---\r\n\r\n');
+writeln('ecwebv4 installer/updater');
+writeln('https://github.com/echicken/synchronet-web-v4');
+write('\r\nIt is strongly recommended that you back up your BBS before proceeding.\r\n\r\n');
+if (deny('Proceed with installation/update')) {
+    writeln('Install/update aborted.  Exiting.');
+    exit();
+}
+write('\r\n\r\n---\r\n\r\n');
+
+writeln('Downloading ' + zip_url);
 if (!download(zip_url, download_target)) {
     writeln('Download of ' + zip_url + ' failed. Exiting.');
     exit();
 }
 
-writeln('Extracting ' + download_target + ' to ' + extract_dir + ' ...');
+writeln('Extracting ' + download_target);
 if (!extract(download_target, extract_dir)) {
     writeln('Extraction of ' + download_target + ' failed. Exiting.');
     exit();
 }
 
-writeln('Updating sbbs.ini ...');
-if (!update_sbbs_ini(root_directory, error_directory)) {
-    writeln('Failed to update sbbs.ini. Exiting.');
-    exit();
-}
+writeln('Copying files ...');
+copy_dir_contents(temp_dir + '/mods', system.mods_dir, true);
+copy_dir_contents(temp_dir + '/text', system.text_dir, true);
+copy_dir_contents(temp_dir + '/web', install_dir, true);
+copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages', false);
+copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages/.examples', true);
+copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar', false);
+copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar/.examples', true);
+
+writeln('Cleaning up ...');
+remove_dir(temp_dir + '/web/pages/.examples');
+remove_dir(temp_dir + '/web/sidebar/.examples');
+remove_dir(temp_dir);
+file_remove(download_target);
 
 modopts_web = get_settings(modopts_web);
 
@@ -115,3 +187,16 @@ if (!update_modopts_ini(modopts_web)) {
     writeln('Failed to update modopts.ini. Exiting.');
     exit();
 }
+
+writeln('Updating sbbs.ini ...');
+if (!update_sbbs_ini(root_directory, error_directory)) {
+    writeln('Failed to update sbbs.ini. Exiting.');
+    exit();
+} else {
+    write('\r\n---\r\n\r\n');
+    writeln('Install/update complete.');
+    writeln('Changes will take effect once your BBS has been restarted.');
+    writeln('For additional configuration and customization steps,');
+    writeln('visit https://github.com/echicken/synchronet-web-v4');
+    write('\r\n\r\n---\r\n\r\n');
+}
-- 
GitLab


From df2fe1cfba8043414eb8a9f72133e132cc777100 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 17:43:51 -0400
Subject: [PATCH 322/752] If ftelnet enabled, prompt for ports and add/update
 WS and WSS sections in services.ini.

---
 mods/webv4-installer.js | 65 +++++++++++++++++++++++++++++++++--------
 1 file changed, 53 insertions(+), 12 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index dd9f7da1b2..2f68d924d6 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -49,6 +49,22 @@ function get_modopts_ini() {
     return ini;
 }
 
+function get_service_ini(section) {
+    const f = new File(system.ctrl_dir + 'services.ini');
+    if (!f.open('r')) return false;
+    const ini = f.iniGetObject(section);
+    f.close();
+    return ini;
+}
+
+function set_service_ini(section, obj) {
+    const f = new File(system.ctrl_dir + 'services.ini');
+    if (!f.open('r+')) return false;
+    if (!f.iniSetObject(section, obj)) return false;
+    f.close();
+    return true;
+}
+
 function get_setting(text, value) {
     const i = prompt(text + ' [' + value + ']');
     return (i == '' ? value : i);
@@ -62,17 +78,6 @@ function confirm_setting(text, value) {
     }
 }
 
-function get_settings(modopts) {
-    write('\r\n---\r\n\r\n');
-    write('Configuration - press enter to accept default/current value.\r\n\r\n');
-    modopts.guest = get_setting('Guest user alias', modopts.guest);
-    modopts.ftelnet = confirm_setting('Enable fTelnet', modopts.ftelnet);
-    if (modopts.ftelnet) modopts.ftelnet_splash = get_setting('Path to ftelnet background .ans', modopts.ftelnet_splash);
-    modopts.user_registration = confirm_setting('Allow new user registration via the web', modopts.user_registration);
-    write('\r\n\r\n---\r\n\r\n');
-    return modopts;
-}
-
 function copy_dir_contents(src, dest, overwrite) {
     src = backslash(fullpath(src));
     dest = backslash(fullpath(dest));
@@ -143,6 +148,24 @@ if (!modopts_web) {
     };
 }
 
+var wss = get_service_ini('WS');
+if (!wss) {
+    var wss = {
+        Port : 1123,
+        Options : 'NO_HOST_LOOKUP',
+        Command : 'websocketservice.js'
+    };
+}
+
+var wsss = get_service_ini('WSS');
+if (!wsss) {
+    var wsss = {
+        Port : 11235,
+        Options : 'NO_HOST_LOOKUP|TLS',
+        Command : 'websocketservice.js'
+    };
+}
+
 write('\r\n---\r\n\r\n');
 writeln('ecwebv4 installer/updater');
 writeln('https://github.com/echicken/synchronet-web-v4');
@@ -180,7 +203,25 @@ remove_dir(temp_dir + '/web/sidebar/.examples');
 remove_dir(temp_dir);
 file_remove(download_target);
 
-modopts_web = get_settings(modopts_web);
+write('\r\n---\r\n\r\n');
+write('Configuration - press enter to accept default/current value.\r\n\r\n');
+modopts_web.guest = get_setting('Guest user alias', modopts_web.guest);
+modopts_web.user_registration = confirm_setting('Allow new user registration via the web', modopts_web.user_registration);
+modopts_web.ftelnet = confirm_setting('Enable fTelnet', modopts_web.ftelnet);
+if (modopts_web.ftelnet) {
+    modopts_web.ftelnet_splash = get_setting('Path to ftelnet background .ans', modopts_web.ftelnet_splash);
+    write('\r\nUse of fTelnet requires a WebSocket proxy service.\r\n');
+    writeln('A websocket proxy server routes traffic between a browser-based');
+    writeln('application and some other arbitrary server.  Here you will configure');
+    writeln('the ports that your WebSocket and WebSocket Secure proxy services will');
+    writeln('listen on. Be sure to open these ports in your firewall.');
+    write('\r\n');
+    wss.Port = get_setting('WebSocket service port for HTTP clients', wss.Port);
+    wsss.Port = get_setting('WebSocket secure service port for HTTPS clients', wsss.Port);
+    writeln('Updating services.ini ...');
+    if(!set_service_ini('WS', wss)) writeln('Failed to configure WS service.');
+    if (!set_service_ini('WSS', wsss)) writeln('Failed to configure WSS service.');
+}
 
 writeln('Updating modopts.ini ...');
 if (!update_modopts_ini(modopts_web)) {
-- 
GitLab


From a7b107ef039ee4937c2004eff6fdecb0fb7c4580 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 17:49:51 -0400
Subject: [PATCH 323/752] Crap pants if guest user don't exist.

---
 mods/webv4-installer.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 2f68d924d6..26276943ac 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -206,6 +206,10 @@ file_remove(download_target);
 write('\r\n---\r\n\r\n');
 write('Configuration - press enter to accept default/current value.\r\n\r\n');
 modopts_web.guest = get_setting('Guest user alias', modopts_web.guest);
+if (!system.matchuser(modopts_web.guest)) {
+    writeln('Guest user does not exist. Exiting.');
+    exit();
+}
 modopts_web.user_registration = confirm_setting('Allow new user registration via the web', modopts_web.user_registration);
 modopts_web.ftelnet = confirm_setting('Enable fTelnet', modopts_web.ftelnet);
 if (modopts_web.ftelnet) {
-- 
GitLab


From ad83e312206395c784fffdb071b631553630ac7f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 17:55:26 -0400
Subject: [PATCH 324/752] Exit if Synchronet version < 3.17.

---
 mods/webv4-installer.js | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 26276943ac..1febaea481 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -169,7 +169,14 @@ if (!wsss) {
 write('\r\n---\r\n\r\n');
 writeln('ecwebv4 installer/updater');
 writeln('https://github.com/echicken/synchronet-web-v4');
+
+if (system.version_num < 31700) {
+    writeln('Synchronet versions earlier than 3.17a are not supported. Exiting.');
+    exit();
+}
+
 write('\r\nIt is strongly recommended that you back up your BBS before proceeding.\r\n\r\n');
+
 if (deny('Proceed with installation/update')) {
     writeln('Install/update aborted.  Exiting.');
     exit();
-- 
GitLab


From 31a05afe713a09c7ad9e4851c7374ed8519cb996 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 18:03:02 -0400
Subject: [PATCH 325/752] Remove sysop-help text; I'll put relevant
 instructions in docs. Do ftelnet things if modopts [web] ftelnet is true.
 Trying to keep this file less cluttered so sysops may find it easier to add
 their own home page content.

---
 web/pages/.examples/000-home.xjs | 131 +++++++++----------------------
 1 file changed, 36 insertions(+), 95 deletions(-)

diff --git a/web/pages/.examples/000-home.xjs b/web/pages/.examples/000-home.xjs
index ca0b275104..6fcb37c903 100644
--- a/web/pages/.examples/000-home.xjs
+++ b/web/pages/.examples/000-home.xjs
@@ -1,103 +1,44 @@
 <!--Home-->
-<?xjs
-	load(settings.web_lib + 'ftelnet.js');
-	var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home');
-
-    var ftelnethelperloaded = false;
-    try {
-      load("ftelnethelper.js");
-      ftelnethelperloaded = true;
-    } catch (e) {
-      // Ignore, we'll display an error below
-    }
+<?xjs var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home'); ?>
 
-    if (!ftelnethelperloaded) {
-?>
-        <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
-<?xjs
-        if (user.security.level >= 90) {
-?>
-            <p>
-                Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
-                <ul>
-                    <li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b> file from CVS</li>
-                </ul>
-            </p>
-<?xjs
-        }
-    } else if (!IsWebSocketServiceEnabled()) {
-?>
-        <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
-<?xjs
-        if (user.security.level >= 90) {
-?>
-            <p>
-                Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
-                <ul>
-                    <li>Enable the WebSocket Proxy Service</li>
-                    <li>Enable the WS and WSS services
-                        <ul>
-                            <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file
-                                <pre>
-                                ;WebSocket service (for fTelnet loaded via http://).
-                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
-                                [WS]
-                                Port=1123
-                                Options=NO_HOST_LOOKUP
-                                Command=websocketservice.js
-                                ;WebSocket Secure service (for fTelnet loaded via https://).
-                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
-                                [WSS]
-                                Port=11235
-                                Options=NO_HOST_LOOKUP | TLS
-                                Command=websocketservice.js
-                                </pre>
-                            </li>
-                        </ul>
-                        <strong>NOTE:</strong> Don't forget to open ports 1123 and 11235 on your firewall and/or add the correct port forwarding rules, if necessary
-                    </li>
-                </ul>
-                <strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).
-            </p>
+<!-- fTelnet -->
 <?xjs
-        }
-    } else {
+    if (settings.ftelnet) {
+        load(settings.web_lib + 'ftelnet.js');
+        load('ftelnethelper.js');
 ?>
-
-<!-- fTelnet -->
-
-    <style>.fTelnetStatusBar { display : none; }</style>
-	<div id="fTelnetContainer" class="fTelnetContainer"></div>
-    <div class="row">
-        <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
-            <button id="ftelnet-connect" class="btn btn-primary">
-                <?xjs write(_hpl.button_ftelnet); ?>
-            </button>
+        <style>.fTelnetStatusBar { display : none; }</style>
+    	<div id="fTelnetContainer" class="fTelnetContainer"></div>
+        <div class="row">
+            <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
+                <button id="ftelnet-connect" class="btn btn-primary">
+                    <?xjs write(_hpl.button_ftelnet); ?>
+                </button>
+            </div>
         </div>
-    </div>
 
-	<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
-	<script>
-        var wsp = <?xjs write(GetWebSocketServicePort()); ?>;
-        var wssp = <?xjs write(GetWebSocketServicePort(true)); ?>;
-		var Options = new fTelnetOptions();
-		Options.BareLFtoCRLF = false;
-		Options.BitsPerSecond = 57600;
-		Options.ConnectionType = 'telnet';
-		Options.Emulation = 'ansi-bbs';
-		Options.Enter = '\r';
-		Options.Font = 'CP437';
-		Options.ForceWss = false;
-		Options.Hostname = '<?xjs write(http_request.vhost); ?>';
-		Options.LocalEcho = false;
-		Options.Port = location.protocol == 'https:' ? wssp : wsp;
-		Options.ScreenColumns = 80;
-		Options.ScreenRows = 25;
-        Options.SplashScreen = '<?xjs write(getSplash()); ?>';
-		var fTelnet = new fTelnetClient('fTelnetContainer', Options);
-        $('#ftelnet-connect').click(function() { fTelnet.Connect(); });
-	</script>
-
-<!-- end fTelnet -->
+    	<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
+    	<script>
+            var wsp = <?xjs write(GetWebSocketServicePort()); ?>;
+            var wssp = <?xjs write(GetWebSocketServicePort(true)); ?>;
+    		var Options = new fTelnetOptions();
+    		Options.BareLFtoCRLF = false;
+    		Options.BitsPerSecond = 57600;
+    		Options.ConnectionType = 'telnet';
+    		Options.Emulation = 'ansi-bbs';
+    		Options.Enter = '\r';
+    		Options.Font = 'CP437';
+    		Options.ForceWss = false;
+    		Options.Hostname = '<?xjs write(http_request.vhost); ?>';
+    		Options.LocalEcho = false;
+    		Options.Port = location.protocol == 'https:' ? wssp : wsp;
+    		Options.ScreenColumns = 80;
+    		Options.ScreenRows = 25;
+            Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+    		var fTelnet = new fTelnetClient('fTelnetContainer', Options);
+            $('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+    	</script>
 
 <?xjs } ?>
+
+<!-- end fTelnet -->
-- 
GitLab


From efd683b10450b1d0da1ea6b83cfdc2aa6593cf1a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 18:04:48 -0400
Subject: [PATCH 326/752] Removed sysop help text; this belongs in docs. I'm
 okay with things crapping out if ftelnethelper.js can't be loaded; that'll be
 documented. No need to evaluate ftelnet enabled/disabled here; sysops should
 hide this page or restrict it via webctrl if they don't want ftelnet on their
 site.

---
 web/pages/.examples/003-games.xjs | 61 +------------------------------
 1 file changed, 1 insertion(+), 60 deletions(-)

diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index 6935ed2879..535593ae83 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -1,6 +1,7 @@
 <!--Games-->
 <?xjs
 
+    load("ftelnethelper.js");
 	load(settings.web_lib + 'ftelnet.js');
 
 	if (typeof settings.xtrn_blacklist === 'string') {
@@ -30,65 +31,7 @@
 		}
 	);
 
-    var options = null;
-    try {
-      load("ftelnethelper.js");
-      options = load("modopts.js", "logon");
-    } catch (e) {
-      // Ignore, just means modopts.js doesn't exist and so an error will be displayed below
-    }
-
-    if (!options) {
-?>
-        <p>Sorry, this BBS is not currently configured in a way that supports this feature.  Please ask the SysOp to enable it!</p>
-<?xjs if (user.security.level >= 90) { ?>
-        <p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Check out the latest <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/ftelnethelper.js'>exec/load/ftelnethelper.js</a></b>, <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/exec/load/modopts.js'>exec/load/modopts.js</a></b> and <b><a href='http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/ctrl/modopts.ini'>ctrl/modopts.ini</a></b> files from CVS</li></ul></p>
-<?xjs
-        }
-    } else if (!options.rlogin_auto_xtrn) {
-        if (user.security.level >= 90) {
-?>
-            <p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Enable the rlogin_auto_xtrn feature of the logon module<ul><li>To do this, ensure that the <b>rlogin_auto_xtrn=</b> line in the <b>[logon]</b> section of <b>sbbs/ctrl/modopts.ini</b> is set to <b>true</b></li><li>(Currently, it's set to <b>" + options.rlogin_auto_xtrn + "</b>)</ul></p>
-<?xjs
-        }
-    } else if (!IsWebSocketServiceEnabled()) {
-        if (user.security.level >= 90) {
-?>
-            <p>
-                Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br />
-                <ul>
-                    <li>Enable the WebSocket Proxy Service</li>
-                    <li>Enable the WS and WSS services
-                        <ul>
-                            <li>To do this, add this block to your <b>sbbs/ctrl/services.ini file
-                                <pre>
-                                ;WebSocket service (for fTelnet loaded via http://).
-                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
-                                [WS]
-                                Port=1123
-                                Options=NO_HOST_LOOKUP
-                                Command=websocketservice.js
-                                ;WebSocket Secure service (for fTelnet loaded via https://).
-                                ;For troubleshooting, please see https://www.ftelnet.ca/synchronet/
-                                [WSS]
-                                Port=11235
-                                Options=NO_HOST_LOOKUP | TLS
-                                Command=websocketservice.js
-                                </pre>
-                            </li>
-                        </ul>
-                        <strong>NOTE:</strong> Don't forget to open ports 1123 and 11235 on your firewall and/or add the correct port forwarding rules, if necessary
-                    </li>
-                </ul>
-                <strong>NOTE:</strong> You may need to tweak the Command= line if your server is listening on a specific IP address (ie change 'localhost' to the correct IP).
-            </p>
-<?xjs
-        }
-    } else if (!IsRLoginEnabled()) {
-        if (user.security.level >= 90) {
 ?>
-        <p>Actually, it looks like you're the SysOp, so here's what you can do to enable it:<br /><ul><li>Allow RLogin connections<ul><li>To do this, ensure that the <b>Options=</b> line in the <b>[BBS]</b> section of <b>sbbs/ctrl/sbbs.ini</b> includes the <b>ALLOW_RLOGIN</b> option.</li><li>Or, if you're running Windows, open the Synchronet Control Panel and click <b>Terminal -> Configure -> RLogin -> Enabled</b> and then click <b>OK</b>.</li></ul></li></ul></p>
-<?xjs } } else { ?>
 
 <style>.fTelnetStatusBar { display : none; }</style>
 
@@ -163,5 +106,3 @@
 	);
 
 </script>
-
-<?xjs } ?>
-- 
GitLab


From 6820c005c2cd0c0103b3e28887e2b4542a450c5e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 31 Mar 2018 18:27:12 -0400
Subject: [PATCH 327/752] Back up services.ini prior to update.

---
 mods/webv4-installer.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 1febaea481..31168111d0 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -230,6 +230,10 @@ if (modopts_web.ftelnet) {
     wss.Port = get_setting('WebSocket service port for HTTP clients', wss.Port);
     wsss.Port = get_setting('WebSocket secure service port for HTTPS clients', wsss.Port);
     writeln('Updating services.ini ...');
+    if (!file_backup(system.ctrl_dir + 'services.ini')) {
+        writeln('Failed to back up services.ini.  Exiting.');
+        exit();
+    }
     if(!set_service_ini('WS', wss)) writeln('Failed to configure WS service.');
     if (!set_service_ini('WSS', wsss)) writeln('Failed to configure WSS service.');
 }
-- 
GitLab


From e9e503f4b1258b5c6d0779e502015d89282f0865 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 00:57:16 -0400
Subject: [PATCH 328/752] If a 'brand' value was provided in modopts.ini [web],
 use it instead of system.name in navbar.

---
 web/root/index.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index afc2ceceda..a8978549c1 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -136,7 +136,7 @@
 						<span class="icon-bar"></span>
 					</button>
 					<a class="navbar-brand" href="./">
-						<?xjs write(system.name); ?>
+						<?xjs write(settings.brand || system.name); ?>
 					</a>
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
-- 
GitLab


From 4b0f7a674389b48ae654c00d943ffbac706031a1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 02:08:55 -0400
Subject: [PATCH 329/752] Documentation has been moved to the repo's wiki.

---
 README.md | 209 +-----------------------------------------------------
 1 file changed, 1 insertion(+), 208 deletions(-)

diff --git a/README.md b/README.md
index b41855c9bd..88a78b9282 100644
--- a/README.md
+++ b/README.md
@@ -1,211 +1,4 @@
 # synchronet-web-v4
 A web interface for Synchronet BBS
 
-* [Disclaimer](#disclaimer)
-* [Requirements](#requirements)
-* [Quick start](#quick-start)
-* [Configuration](#configuration)
-	* [Advanced / Optional Settings](#optional-settings)
-* [Customization](#customization)
-	* [Sidebar Modules](#sidebar-modules)
-	* [Pages](#pages)
-* [Uninstall](#uninstall)
-* [Getting Support](#getting-support)
-* [FAQ](#faq)
-
-### Disclaimer
-
-- Use this software at your own risk.  It's still being developed, and hasn't been thoroughly tested yet.
-
-### Requirements
-
-- This web interface has been tested with Synchronet BBS 3.17a.  It will probably work with later versions.
-- In addition to updating your Synchronet build, be sure to update your the javascript modules in your *exec* and *exec/load* directories as well.  You can get them from [the Synchronet CVS repository](http://cvs.synchro.net/) or in the [sbbs_run.zip archive](http://vert.synchro.net/Synchronet/sbbs_run.zip).
-	- Do *not* simply unzip sbbs_run.zip overtop your existing sbbs directory structure.  This may overwrite much of your local configuration.  Extract the archive elsewhere and copy things over piecemeal as needed.
-
-### Quick start
-
-- Back up your Synchronet installation (particularly 'ctrl' and 'web')
-- Shut down your BBS
-- Clone [or download an archive of](https://github.com/echicken/synchronet-web-v4/archive/master.zip) this repository to a convenient location
-	- Copy the contents of the downloaded *mods* directory into your local *mods* directory
-	- Copy the contents of the downloaded *text* directory into your local *text* directory
-	- Rename your current *web* directory to something like *web.old* and then copy the downloaded *web* directory in its place
-- Add the following section to your *ctrl/modopts.ini* file:
-```ini
-[web]
-	; Unauthenticated visitors will be logged in as the user with this alias
-	; (Only give this user privileges you want unknown web visitors to have)
-	guest = Guest
-	; Login sessions expire after this many seconds of inactivity
-	timeout = 43200
-	; Web-based users disappear from the "Who's online" list after this many seconds
-	inactivity = 900
-	; Allow new users to register via the web interface
-	user_registration = true
-	; Enforce a minimum password length if user_registration is true
-	minimum_password_length = 6
-	; Limit the length of a telegram (in characters) that a web user can send
-	maximum_telegram_length = 800
-	; Where (absolute or relative to 'exec') the 'lib' and 'root' directories live
-	web_directory = ../web
-    ; Path to a .ans file to use as the ftelnet splash screen
-    ftelnet_splash = ../text/synch.ans
-	; Enable or disable keyboard navigation in message threads
-	keyboard_navigation = false
-	; Display upvote/downvote buttons in message threads (3.17)
-	vote_functions = true
-	; Refresh nodelist, vote counts, etc. this often (in milliseconds)
-	refresh_interval = 60000
-    ; External Programs (or entire sections) to exclude from the Games page
-    xtrn_blacklist = scfg,oneliner
-	; Disable the sidebar altogether
-	layout_sidebar_off = false
-	; Place the sidebar on the left-hand side of the page
-	layout_sidebar_left = false
-	; Make the page consume the entire width of the browser window
-	layout_full_width = false
-```
-
-- Tell your router and firewall to open and forward ports *1123* and *1513* to your BBS
-- If you were running ecWeb v3 and modified the *RootDirectory* value in the *[Web]* section of *ctrl/sbbs.ini* to point to *../web/root/ecwebv3*, change it back to *../web/root*.
-- Start your BBS back up again
-
-### Configuration
-
-- Ensure that the *guest* user specified in the [web] section of *ctrl/modopts.ini* exists and has only the permissions that you want an unauthenticated visitor from the web to have.  This user probably shouldn't be able to post messages, and definitely shouldn't be able to post to networked message areas.
-- Customise the *xtrn_blacklist* setting in the [web] section of *ctrl/modopts.ini*.  This is a comma-separated list of *internal codes* of any programs (or Online Program Sections) that you wish to *exclude* from your games page.
-
-#### Optional Settings
-
-The following *optional* settings can be added to the [web] section of your *ctrl/modopts.ini* file:
-
-```ini
-	; Use extended-ASCII characters when displaying forum messages (default: true)
-	forum_extended_ascii = false
-	; Only load this many messages from each sub (default: 0 for all)
-	max_messages = 500
-	; Serve this web interface from a subdirectory of the webserver's document root
-	web_root = ../web/root/ecwebv4
-```
-
-- Setting *forum_extended_ascii* to *false* may resolve problems with displaying UTF-8 encoded characters; character codes > 127 will not be assumed to be extended-ASCII references to CP437 characters.
-- Tweaking *max_messages* may improve forum performance or resolve 'Out of memory' errors, if you see any of those when browsing the forum
-- If you placed the *contents* of the *web/root/* directory from this repository at some other location within your webserver's document root, use the *web_root* value to point to that directory.  (Note that the *contents* of the *web/lib/* directory from this repository must still live at *[web_directory]/lib* on your system.)
-
-### Customization
-
-This web interface uses [Bootstrap 3.3.5](http://getbootstrap.com/).  It should be possible to use any compatible stylesheet.
-
-- You can place your own CSS overrides in *web/root/css/custom.css*.  Create this file and use it if you want to use fonts, colours, etc. other than the defaults.  It is not recommended that you modify any of the existing stylesheets.
-
-#### Sidebar Modules
-
-Sidebar modules are the widgets displayed in the narrow column running down the right (or left) side of the page.  A sidebar module can be an SSJS, XJS, HTML, or TXT file.
-
-- Sidebar modules are loaded in alphanumeric order from the *web/root/sidebar/* directory; see the included files for examples and for a file-naming convention that enforces order of appearance
-- HTML, XJS, and SSJS sidebar modules should not be complete HTML documents.  They should not have \<html\>, \<head\>, or \<body\> tags.  Instead, they should contain (or produce) an HTML snippet suitable for inclusion in the overall page.
-- TXT sidebar modules are displayed inside of \<pre\> tags to preserve fixed-width formatting.
-- Support for additional file formats can be added if necessary, but by using HTML and Javascript you should be able to display whatever you like.  (If you want to put an image in the sidebar, a simple HTML file containing an \<img\> tag will do the job, for example.)
-
-#### Pages
-
-Like sidebar modules, pages can be HTML, XJS, SSJS, or TXT files.
-
-- Pages are loaded in alphanumeric order from the *web/root/pages/* directory.  See the included files for examples and for a file-naming convention that enforces order of appearance.
-- As with sidebar modules, HTML, XJS, and SSJS pages should not be complete HTML documents.  They should contain (or generate) snippets of text or HTML suitable for inclusion in the overall page.
-- Your *web/root/pages/* directory should contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file.
-	- You can use this file to restrict access to certain pages so that guests or unprivileged users can't see them
-- In an HTML, XJS, or SSJS file, the first line *containing a comment* is treated as the *control line* for the page.
-	- The format of the control line is *OPTION|OPTION:Title*
-	- Available options at the moment are *HIDDEN* and *NO_SIDEBAR*
-	- *HIDDEN* pages will not appear in the menus or in the activity fields of the *who's online* list
-	- The *NO_SIDEBAR* directive tells the layout script not to include a sidebar on this page
-		- The main content will expand to fill the space normally used by the sidebar
-	- The part of the control line following the first colon is treated as the title of the page.  This is the text that appears in the menu, browser title bar, and *who's online* list.
-		- If you don't need to specify any options, (and as long as your page title doesn't contain a colon) you can omit the colon, and the entire string will be treated as the title of the page, or you can start the control line with a colon.
-
-Here's an example control line for a hidden HTML file:
-
-```html
-<!--HIDDEN:My Awesome Web Page-->
-```
-
-Here's an example control line for a hidden XJS page with no sidebar:
-
-```html
-<!--HIDDEN|NO_SIDEBAR:My Awesome Web Page-->
-```
-
-Here's an example control line for an SSJS script with no settings:
-
-```js
-//My Awesome Web Page
-```
-
-If your page title contains a colon, it's necessary to prepend a dummy one like so:
-
-```js
-//:Awesome Web Pages: This is one
-```
-
-Of course, that's not an issue if you're providing some settings:
-
-```html
-<!--NO_SIDEBAR:Awesome Web Pages: This is one-->
-```
-
-You can add drop-down menus to the navigation bar by adding subdirectories to the *web/root/pages/* directory.  The files within these subdirectories follow the same format described above.
-- The name of the subdirectory is used as the text of the menu item.
-- You can nest additional subdirectories to create sub-menus.
-- Subdirectories with names beginning with *.* will be ignored.
-- Each subdirectory can contain a [webctrl.ini](http://wiki.synchro.net/server:web#webctrlini_per-directory_configuration_file) file for access control.
-
-### Uninstall
-
-To stop using this web interface, you can just revert to your previous *web* directory at any time.
-
-- The [web] section added to *ctrl/modopts.ini* won't hurt anything if you leave it there, but you can delete it if you want
-- Revert your *ctrl/services.ini* file to the backup you made prior to installing this web interface
-- Undo any changes you made to your firewall & router during the *Quick Start*
-
-### Getting Support
-
-#### Via GitHub
-
-- Please browse the existing [issues](https://github.com/echicken/synchronet-web-v4/issues) for this project, including those marked as closed.  You may find that your question has already been asked (and hopefully answered).
-- Open a new [issue](https://github.com/echicken/synchronet-web-v4/issues) here on GitHub
-
-#### On IRC
-
-- You can find me in #synchronet on irc.synchro.net.  I may be AFK, but ask your question and idle for a while; I'll respond eventually.
-
-#### On DOVE-Net
-
-- Post a message to 'echicken' in the Synchronet Sysops area.
-
-#### Email
-
-- Please don't.  Public support discussions are better for everyone, especially those searching the web for answers in the future.
-
-### FAQ
-
-#### I followed the instructions, but it isn't working
-
-It usually turns out that the instructions were not followed closely enough.  Please try again.  (I'm not pretending that this thing is bug-free or that the instructions are perfectly clear.  I'm just sayin'.)
-
-#### System performance is poor / memory usage has increased since I installed this
-
-The Forum page is the most likely culprit.  Sorting messages into threads takes up a certain amount of memory and CPU time.  Bots crawling through your Forum may hit several pages at once, etc.  You may be able to mitigate this by playing with the *max_messages* setting as [described above](#optional-settings).  You may also consider consider restricting access to your Forum page so that only authenticated / non-guest users may browse it; access control via *webctrl.ini* files is described in the [Pages](#pages) section above.
-
-#### Something related to Fidonet isn't working!
-
-I don't care.
-
-#### Why aren't you using *insert flavour-of-the-month web framework here*
-
-Because I don't feel like it, and I don't want to hear about it.  I'm sure it's awesome, though.
-
-#### Why don't you use *insert javascript-tooling-thing here*
-
-Because I don't feel like it.  Also, if it adds additional dependencies or setup steps on behalf of the end-user (sysops), I'm not interested in supporting it.
+Documentation is available on [the wiki](https://github.com/echicken/synchronet-web-v4/wiki)
-- 
GitLab


From 898ed47c1f2af44333d4a18f626b7eca4121c82e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:13:41 -0400
Subject: [PATCH 330/752] Change the way initial settings are loaded to better
 find the place where things are actually installed.

---
 web/lib/init.js               |  4 ----
 web/root/api/attachments.ssjs |  8 ++++++--
 web/root/api/auth.ssjs        |  5 ++++-
 web/root/api/files.ssjs       |  7 +++++--
 web/root/api/forum.ssjs       | 37 +++++++++++++++++++----------------
 web/root/api/github.ssjs      |  9 ++++++---
 web/root/api/register.ssjs    |  5 ++++-
 web/root/api/system.ssjs      |  7 +++++--
 web/root/index.xjs            |  5 ++++-
 9 files changed, 54 insertions(+), 33 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index 0d8c54377c..bf24e9ba7e 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,7 +1,3 @@
-load('modopts.js');
-
-var settings = get_mod_options('web');
-
 // Paths
 settings.web_directory = fullpath(
 	backslash(
diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index cd3777acf9..e79a56feb7 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -1,5 +1,9 @@
 load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
+
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'mime-decode.js');
 
@@ -57,4 +61,4 @@ if (typeof att != 'undefined') {
 	}
 	http_reply.header['Content-Length'] = att.body.length;
 	write(att.body);
-}
\ No newline at end of file
+}
diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 7919e8ac92..1f47e86774 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -1,4 +1,7 @@
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 load(settings.web_lib + 'auth.js');
 
 var response = JSON.stringify(
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 1fcdd98cc6..bef2f62626 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,4 +1,7 @@
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'files.js');
 load('filebase.js');
@@ -56,4 +59,4 @@ if (!reply) exit();
 reply = JSON.stringify(reply);
 http_reply.header['Content-Type'] = 'application/json';
 http_reply.header['Content-Length'] = reply.length;
-write(reply);
\ No newline at end of file
+write(reply);
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index fe15e56154..00e878fc9b 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -3,7 +3,10 @@
     is done here; otherwise all permission checking is done at the function
     level. */
 
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'forum.js');
 
@@ -26,17 +29,17 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
             case 'get-mail-unread-count':
                 reply.count = getMailUnreadCount();
                 break;
-            
+
             case 'get-mail-body':
                 if (typeof http_request.query.number !== 'undefined') {
                     reply = getMailBody(http_request.query.number[0]);
                 }
                 break;
-            
+
             case 'get-signature':
                 reply.signature = getSignature();
                 break;
-            
+
             case 'post-reply':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.body !== 'undefined' &&
@@ -51,7 +54,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     reply.success = false;
                 }
                 break;
-            
+
             case 'post':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.to !== 'undefined' &&
@@ -68,7 +71,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     reply.success = false;
                 }
                 break;
-            
+
             case 'delete-message':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.number !== 'undefined'
@@ -153,14 +156,14 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
             default:
                 handled = false;
                 break;
-                
+
         }
 
     }
 
     // Unauthenticated calls
     if (!handled) {
-    
+
         switch(http_request.query.call[0].toLowerCase()) {
 
             case 'get-thread-votes':
@@ -176,13 +179,13 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     }
                 }
                 break;
-            
+
             case 'get-sub-votes':
                 if (typeof http_request.query.sub !== 'undefined') {
                     reply = getVotesInThreads(http_request.query.sub[0]);
                 }
                 break;
-            
+
             case 'get-poll-results':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.id !== 'undefined'
@@ -197,13 +200,13 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
             case 'list-groups':
                 reply = listGroups();
                 break;
-            
+
             case 'list-subs':
                 if (typeof http_request.query.group !== 'undefined') {
                     reply = listSubs(http_request.query.group[0]);
                 }
                 break;
-            
+
             case 'list-threads':
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.offset !== 'undefined'
@@ -218,7 +221,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     );
                 }
                 break;
-            
+
             case 'get-group-unread-count':
                 if (typeof http_request.query.group !== 'undefined') {
                     http_request.query.group.forEach(
@@ -238,12 +241,12 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     );
                 }
                 break;
-            
+
             default:
                 break;
-        
+
         }
-    
+
     }
 
 }
@@ -251,4 +254,4 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 reply = JSON.stringify(reply);
 http_reply.header['Content-Type'] = 'application/json';
 http_reply.header['Content-Length'] = reply.length;
-write(reply);
\ No newline at end of file
+write(reply);
diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 168a8b693b..87e4995fcb 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -22,7 +22,10 @@ load('sbbsdefs.js');
 load('hmac.js');
 var options = load({}, 'modopts.js', 'github_notify');
 
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 
 function b2h(str) {
 	return str.split('').map(
@@ -62,7 +65,7 @@ var header = {
 var body = payload.commits.map(
 	function (e) {
 		var ret = [
-			'Commit ID: ' + e.id, 
+			'Commit ID: ' + e.id,
 			'Author: ' + e.author.username,
 		];
 		if (e.added.length > 0) ret.push('Added: ' + e.added.join(', '));
@@ -86,4 +89,4 @@ subs.forEach(
 			log(LOG_ERR, err);
 		}
 	}
-);
\ No newline at end of file
+);
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 8954a1f96d..9fd84f7f28 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,5 +1,8 @@
 load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 load(settings.web_lib + '/auth.js');
 load(settings.web_lib + '/language.js');
 
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 48f0e30046..14bb6ec11f 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -1,6 +1,9 @@
 load('sbbsdefs.js');
 load('nodedefs.js');
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_lib + 'init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'language.js');
 
@@ -98,4 +101,4 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 reply = JSON.stringify(reply);
 http_reply.header['Content-Type'] = 'application/json';
 http_reply.header['Content-Length'] = reply.length;
-write(reply);
\ No newline at end of file
+write(reply);
diff --git a/web/root/index.xjs b/web/root/index.xjs
index a8978549c1..9b3a431733 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,7 +1,10 @@
 <?xjs
 
+    load('modopts.js');
+    var settings = get_mod_options('web');
+
 	load('xjs.js');
-	load(system.exec_dir + '../web/lib/init.js');
+	load(settings.web_lib + 'init.js');
 	load(settings.web_lib + 'auth.js');
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
-- 
GitLab


From e461938257448f2345b955daa74e062fc8feeab4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:19:52 -0400
Subject: [PATCH 331/752] blah.

---
 web/lib/init.js               | 2 ++
 web/root/api/attachments.ssjs | 2 +-
 web/root/api/auth.ssjs        | 2 +-
 web/root/api/files.ssjs       | 2 +-
 web/root/api/forum.ssjs       | 2 +-
 web/root/api/github.ssjs      | 2 +-
 web/root/api/register.ssjs    | 2 +-
 web/root/api/system.ssjs      | 2 +-
 web/root/index.xjs            | 2 +-
 9 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index bf24e9ba7e..ddb155b1a8 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -14,6 +14,8 @@ settings.web_root = fullpath(
 	)
 );
 settings.web_lib = backslash(settings.web_directory + 'lib/');
+settings.web_pages = fullpath(backslash(settings.web_directory + 'pages'));
+settings.web_sidebar = fullpath(backslash(settings.web_directory + 'pages'));
 
 // Guest
 if (typeof settings.guest === 'undefined') settings.guest = 'Guest';
diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index e79a56feb7..41d348e5f7 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -3,7 +3,7 @@ load('sbbsdefs.js');
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'mime-decode.js');
 
diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 1f47e86774..d3538ee29e 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -1,7 +1,7 @@
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 
 var response = JSON.stringify(
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index bef2f62626..d049ddb56d 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,7 +1,7 @@
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'files.js');
 load('filebase.js');
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 00e878fc9b..5d31b9c20c 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -6,7 +6,7 @@
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'forum.js');
 
diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 87e4995fcb..36a8dab5a9 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -25,7 +25,7 @@ var options = load({}, 'modopts.js', 'github_notify');
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 
 function b2h(str) {
 	return str.split('').map(
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 9fd84f7f28..0561c6e3c3 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -2,7 +2,7 @@ load('sbbsdefs.js');
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + '/auth.js');
 load(settings.web_lib + '/language.js');
 
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 14bb6ec11f..102838fbcd 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -3,7 +3,7 @@ load('nodedefs.js');
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'language.js');
 
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 9b3a431733..e75cdd5747 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -4,7 +4,7 @@
     var settings = get_mod_options('web');
 
 	load('xjs.js');
-	load(settings.web_lib + 'init.js');
+	load(settings.web_directory + '/lib/init.js');
 	load(settings.web_lib + 'auth.js');
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
-- 
GitLab


From b13a2991224a43cb0a8fd9ac62e8c60d1e8fed0a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:21:34 -0400
Subject: [PATCH 332/752] blah

---
 web/pages/.examples/001-forum.ssjs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web/pages/.examples/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
index b45107a4fe..7f52efad15 100644
--- a/web/pages/.examples/001-forum.ssjs
+++ b/web/pages/.examples/001-forum.ssjs
@@ -1,7 +1,9 @@
 //Forum
 
 load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'forum.js');
 load(settings.web_lib + 'avatars.js');
 
-- 
GitLab


From 3298f0a1a243688ee9c8cc927fa13c8ec0b85659 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:24:42 -0400
Subject: [PATCH 333/752] More path fixes.

---
 web/lib/forum.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index d5c267533e..2dad427b9f 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,5 +1,8 @@
 load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
+load('modopts.js');
+var settings = get_mod_options('web');
+
+load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
 
 function listGroups() {
-- 
GitLab


From 0a84ffbf7f2e76ecc2acae28f4aeab364115757b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:27:30 -0400
Subject: [PATCH 334/752] Correct path to sidebar modules.

---
 web/lib/init.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index ddb155b1a8..37fa1848ce 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -15,7 +15,7 @@ settings.web_root = fullpath(
 );
 settings.web_lib = backslash(settings.web_directory + 'lib/');
 settings.web_pages = fullpath(backslash(settings.web_directory + 'pages'));
-settings.web_sidebar = fullpath(backslash(settings.web_directory + 'pages'));
+settings.web_sidebar = fullpath(backslash(settings.web_directory + 'sidebar'));
 
 // Guest
 if (typeof settings.guest === 'undefined') settings.guest = 'Guest';
-- 
GitLab


From b7d7a374e46fb5ac4374c5af8de6432f3d12b3a7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:28:37 -0400
Subject: [PATCH 335/752] Load settings and init script properly.

---
 web/pages/.examples/002-files.xjs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index 99c49b33c9..e97f0ca7e7 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -1,7 +1,9 @@
 <!--Files-->
 
 <?xjs
-	load(system.exec_dir + '../web/lib/init.js');
+    load('modopts.js');
+    var settings = get_mod_options('web');
+    load(settings.web_directory + '/lib/init.js');
 	load(settings.web_lib + 'files.js');
 
 	var _fpl = getLanguage(settings.language_file || 'english.ini', 'page_files');
-- 
GitLab


From ec116de0560ca668a196126512dd8c3a27192ed6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:31:59 -0400
Subject: [PATCH 336/752] Settings and init have already been loaded by this
 point, so don't bother.

---
 web/pages/.examples/001-forum.ssjs | 3 ---
 web/pages/.examples/002-files.xjs  | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/web/pages/.examples/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
index 7f52efad15..c66b57f7d5 100644
--- a/web/pages/.examples/001-forum.ssjs
+++ b/web/pages/.examples/001-forum.ssjs
@@ -1,9 +1,6 @@
 //Forum
 
 load('sbbsdefs.js');
-load('modopts.js');
-var settings = get_mod_options('web');
-load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'forum.js');
 load(settings.web_lib + 'avatars.js');
 
diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index e97f0ca7e7..8bd7406f34 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -1,9 +1,6 @@
 <!--Files-->
 
 <?xjs
-    load('modopts.js');
-    var settings = get_mod_options('web');
-    load(settings.web_directory + '/lib/init.js');
 	load(settings.web_lib + 'files.js');
 
 	var _fpl = getLanguage(settings.language_file || 'english.ini', 'page_files');
-- 
GitLab


From 2b840cfa38dd6acc9cf19f5261ee3ce2756dbcf8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 1 Apr 2018 14:41:13 -0400
Subject: [PATCH 337/752] Notes re: shutting down the BBS.

---
 mods/webv4-installer.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 31168111d0..fa79a6796e 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -175,7 +175,8 @@ if (system.version_num < 31700) {
     exit();
 }
 
-write('\r\nIt is strongly recommended that you back up your BBS before proceeding.\r\n\r\n');
+write('\r\nIt is strongly recommended that you back up your BBS before proceeding.\r\n');
+write('\r\nIf this is a new intallation, you must also shut down your BBS now.\r\n\r\n');
 
 if (deny('Proceed with installation/update')) {
     writeln('Install/update aborted.  Exiting.');
@@ -251,7 +252,7 @@ if (!update_sbbs_ini(root_directory, error_directory)) {
 } else {
     write('\r\n---\r\n\r\n');
     writeln('Install/update complete.');
-    writeln('Changes will take effect once your BBS has been restarted.');
+    writeln('If you shut down your BBS, you can restart it now.');
     writeln('For additional configuration and customization steps,');
     writeln('visit https://github.com/echicken/synchronet-web-v4');
     write('\r\n\r\n---\r\n\r\n');
-- 
GitLab


From 4306898fca3e7e38d4aa429ffeb2f955614eb572 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 24 Jul 2018 08:10:19 -0400
Subject: [PATCH 338/752] Reuse User object.  This file needs some more fixing
 later. This file needs to be copied out of the .examples folder to take
 effect.

---
 web/pages/.examples/More/001-userlist.xjs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/pages/.examples/More/001-userlist.xjs b/web/pages/.examples/More/001-userlist.xjs
index e3645277c6..fca38c012e 100644
--- a/web/pages/.examples/More/001-userlist.xjs
+++ b/web/pages/.examples/More/001-userlist.xjs
@@ -140,8 +140,9 @@
 
 	function loadUsers(offset, pageSize) {
 		var users = [];
+    var usr = new User(1);
 		for (var u = 1; u < system.lastuser; u++) {
-			var usr = new User(u);
+			usr.number = u;
 			if (usr.settings&USER_DELETED ||
 				usr.compare_ars('REST Q') ||
 				usr.alias === 'Guest' ||
-- 
GitLab


From 12c324443286940f1bd05960ab0cd0290da94e83 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 24 Jul 2018 10:19:17 -0400
Subject: [PATCH 339/752] Reuse the same user object.

---
 web/root/api/system.ssjs | 39 +++++++++++++++------------------------
 1 file changed, 15 insertions(+), 24 deletions(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 102838fbcd..2ebcc4688f 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -19,22 +19,15 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	switch (http_request.query.call[0]) {
 
 		case 'node-list':
-			reply = system.node_list.map(
-				function (node) {
-					if (node.status === 3) var usr = new User(node.useron);
-					return (
-						{	status : format(
-								NodeStatus[node.status], node.aux, node.extaux
-							),
-							action : format(
-								NodeAction[node.action], node.aux, node.extaux
-							),
-							user : (typeof usr === 'undefined' ? '' : usr.alias)
-						}
-					);
-				}
-			);
-			var usr = new User(1);
+      var usr = new User(1);
+			reply = system.node_list.map(function (node) {
+        usr.number = node.useron;
+				return ({
+          status : format(NodeStatus[node.status], node.aux, node.extaux),
+					action : format(NodeAction[node.action], node.aux, node.extaux),
+          user : (node.status == 3 ? usr.alias : '')
+				});
+			});
 			for (var un = 1; un < system.lastuser; un++) {
 				usr.number = un;
 				if (usr.connection !== 'HTTP') continue;
@@ -43,12 +36,11 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				if (usr.logontime < time() - settings.inactivity) continue;
 				var webAction = getSessionValue(usr.number, 'action');
 				if (webAction === null) continue;
-				reply.push(
-					{	status : '',
-						action : _language.nodelist_action_prefix + ' ' + webAction,
-						user : usr.alias
-					}
-				);
+				reply.push({
+          status : '',
+					action : _language.nodelist_action_prefix + ' ' + webAction,
+					user : usr.alias
+				});
 			}
 			break;
 
@@ -68,8 +60,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			var un = system.matchuser(http_request.query.user[0]);
 			if (un < 1) break;
 			system.put_telegram(
-				un,
-				format(
+				un, format(
 					_language.telegram_header_format,
 					user.alias, (new Date()).toLocaleString()
 				) + '\r\n' + http_request.query.telegram[0] + '\r\n'
-- 
GitLab


From 1dee7b77290defdb0c2012a12d381939d2fcec8e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 24 Jul 2018 18:40:25 -0400
Subject: [PATCH 340/752] Reuse the same User instance where applicable,
 changing number to load a new record. Dereference User instance when it's no
 longer needed. This may help with the old too-many-open-files thing. NB:
 web/pages/.examples/More/001-userlist.xjs must be copied to a production
 location, default would be web/pages/More/001-userlist.xjs, if you want a
 userlist page.

---
 web/lib/auth.js                           | 12 +++++++++---
 web/pages/.examples/More/001-userlist.xjs |  1 +
 web/root/api/system.ssjs                  |  3 ++-
 3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index f17551d28b..2164aa5313 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -48,6 +48,7 @@ function setCookie(usr, sessionKey) {
 
 function validateSession(cookies) {
 
+  var usr = new User(0);
 	for (var c in cookies) {
 
 		if (cookies[c].search(/^\d+,\w+$/) < 0) continue;
@@ -55,7 +56,7 @@ function validateSession(cookies) {
 		var cookie = cookies[c].split(',');
 
 		try {
-			var usr = new User(cookie[0]);
+			usr.number = cookie[0];
 			if (usr.number < 1) {
 				throw 'Invalid user number ' + cookie[0] + ' in cookie.';
 			}
@@ -70,17 +71,20 @@ function validateSession(cookies) {
 			continue;
 		}
 
-		authenticate(usr.alias, usr.security.password);
+		var _usr = authenticate(usr.alias, usr.security.password);
+    _usr = undefined;
 		setCookie(usr, session.key);
 		setSessionValue(usr.number, 'ip_address', client.ip_address);
 		break;
 
 	}
+  usr = undefined;
 
 }
 
 function destroySession(cookies) {
 
+  var usr = new User(0);
 	for (var c in cookies) {
 
 		if (cookies[c].search(/^\d+,\w+$/) < 0)	continue;
@@ -89,7 +93,7 @@ function destroySession(cookies) {
 
 		try {
 
-			var usr = new User(cookie[0]);
+			usr.number = cookie[0];
 			if(usr.number < 1) {
 				throw 'Invalid user number ' + cookie[0] + ' in cookie.';
 			}
@@ -123,6 +127,7 @@ function destroySession(cookies) {
 		}
 
 	}
+  usr = undefined;
 
 }
 
@@ -174,6 +179,7 @@ if (user.number === 0) {
 	if (gn > 0) {
 		var gu = new User(gn);
 		login(gu.alias, gu.security.password);
+    gu = undefined;
 	} else {
 		// Otherwise just kill the script, for security's sake
 		exit();
diff --git a/web/pages/.examples/More/001-userlist.xjs b/web/pages/.examples/More/001-userlist.xjs
index fca38c012e..bf63f03c7a 100644
--- a/web/pages/.examples/More/001-userlist.xjs
+++ b/web/pages/.examples/More/001-userlist.xjs
@@ -152,6 +152,7 @@
 			}
 			users.push(copyProperties(usr, {}));
 		}
+    usr = undefined;
 		users.sort(sortUsers);
 		return users.slice(offset, offset + pageSize);
 	}
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 2ebcc4688f..34a888720e 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -19,7 +19,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	switch (http_request.query.call[0]) {
 
 		case 'node-list':
-      var usr = new User(1);
+      var usr = new User(0);
 			reply = system.node_list.map(function (node) {
         usr.number = node.useron;
 				return ({
@@ -42,6 +42,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 					user : usr.alias
 				});
 			}
+      usr = undefined;
 			break;
 
 		case 'send-telegram':
-- 
GitLab


From 7bfffb5c80fecffc6e6cde63f532b7511be163ec Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 24 Jul 2018 22:16:22 -0400
Subject: [PATCH 341/752] Accept a git tag as an argument to the script. 
 sbbs317 will download the Synchronet BBS 3.17 compatible release.  If absent,
 the master archive will be fetched.

---
 mods/webv4-installer.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index fa79a6796e..38f9bf3a1e 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -116,7 +116,8 @@ function remove_dir(dir) {
     rmdir(dir);
 }
 
-const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/master';
+const url_suffix = argc > 0 ? argv[0] : master;
+const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/' + url_suffix;
 const download_target = system.temp_dir + 'webv4.zip';
 const extract_dir = fullpath(system.temp_dir);
 const temp_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
-- 
GitLab


From 8bcd5b1f3f80688a48e30b27b63728a2f6c978d5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 25 Jul 2018 00:49:17 -0400
Subject: [PATCH 342/752] Fixed bug in last commit (url_suffix 'master') Crappy
 argument parsing.  Command line parameters are now: -release=sbbs317
 -defaults If -release omitted, 'master' archive will be fetched. If -defaults
 specified, installation will proceed without prompts and all default values
 will be accepted.

---
 mods/webv4-installer.js | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 38f9bf3a1e..2224686e21 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -1,3 +1,11 @@
+const named_parameters = {};
+if (argv.length > 0) {
+  argv.forEach(function (e) {
+    const m = /-?((\w+)(?:=("[^"]+"|[^\s"]+))?)(?:\s+|$)/.exec(e);
+    named_parameters[m[2]] = (m[3] ? m[3] : null);
+  });
+}
+
 load('http.js');
 
 function download(url, target) {
@@ -66,16 +74,19 @@ function set_service_ini(section, obj) {
 }
 
 function get_setting(text, value) {
-    const i = prompt(text + ' [' + value + ']');
-    return (i == '' ? value : i);
+  if (typeof named_parameters.defaults != 'undefined') return value;
+  const i = prompt(text + ' [' + value + ']');
+  return (i == '' ? value : i);
 }
 
 function confirm_setting(text, value) {
-    if (!value) {
-        return !deny(text + ' [' + value + ']');
-    } else {
-        return confirm(text + ' [' + value + ']');
-    }
+  if (typeof named_parameters.defaults != 'undefined') {
+    return value;
+  } else if (!value) {
+    return !deny(text + ' [' + value + ']');
+  } else {
+    return confirm(text + ' [' + value + ']');
+  }
 }
 
 function copy_dir_contents(src, dest, overwrite) {
@@ -116,7 +127,7 @@ function remove_dir(dir) {
     rmdir(dir);
 }
 
-const url_suffix = argc > 0 ? argv[0] : master;
+const url_suffix = named_parameters.release ? named_parameters.release : 'master';
 const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/' + url_suffix;
 const download_target = system.temp_dir + 'webv4.zip';
 const extract_dir = fullpath(system.temp_dir);
@@ -135,7 +146,7 @@ if (!modopts_web) {
     	minimum_password_length : 6,
     	maximum_telegram_length : 800,
     	web_directory : install_dir,
-        ftelnet : true,
+      ftelnet : true,
     	ftelnet_splash : '../text/synch.ans',
     	keyboard_navigation : false,
     	vote_functions : true,
@@ -179,7 +190,7 @@ if (system.version_num < 31700) {
 write('\r\nIt is strongly recommended that you back up your BBS before proceeding.\r\n');
 write('\r\nIf this is a new intallation, you must also shut down your BBS now.\r\n\r\n');
 
-if (deny('Proceed with installation/update')) {
+if (typeof named_parameters.defaults == 'undefined' && deny('Proceed with installation/update')) {
     writeln('Install/update aborted.  Exiting.');
     exit();
 }
-- 
GitLab


From f6c6522739bf3e1c8a818c065e6c845947c78183 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 29 Jul 2018 21:36:08 -0400
Subject: [PATCH 343/752] Just unzip

---
 mods/webv4-installer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 2224686e21..2015ae7d83 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -27,7 +27,7 @@ function extract(file, target) {
 	if (!file_isdir(install_dir)) {
         if (!mkdir(install_dir)) return false;
     }
-	return system.exec(system.exec_dir + 'unzip -uqo ' + file + ' -d ' + target) == 0;
+	return system.exec('unzip -uqo ' + file + ' -d ' + target) == 0;
 }
 
 function update_sbbs_ini(root_directory, error_directory) {
-- 
GitLab


From 7f47452ca8bb4f2c266301eb999f0920fce04504 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 29 Jul 2018 21:45:27 -0400
Subject: [PATCH 344/752] error directory

---
 mods/webv4-installer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 2015ae7d83..3612e0cfb8 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -134,7 +134,7 @@ const extract_dir = fullpath(system.temp_dir);
 const temp_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
 const install_dir = fullpath(system.exec_dir + '../webv4');
 const root_directory = fullpath(install_dir + '/root');
-const error_directory = fullpath(root_directory + '/errors');
+const error_directory = fullpath(root_directory + '/error');
 
 var modopts_web = get_modopts_ini();
 if (!modopts_web) {
-- 
GitLab


From 63e754831cff4a497de5366c82e37fc752bff3cf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 30 Jul 2018 10:47:42 -0400
Subject: [PATCH 345/752] 'pages' directory location has changed; look for
 webctrl there.

---
 web/root/index.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index e75cdd5747..adbf84f8d2 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -31,7 +31,7 @@
 
 	function writePage() {
 		var ini = getWebCtrl(
-			settings.web_root + 'pages/' + page.replace(file_getname(page), '')
+			settings.web_pages +  '/' + page.replace(file_getname(page), '')
 		);
 		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
 			write(getPage(page));
-- 
GitLab


From 8feca85cda64094b2028fdaf4699f6f5582a14e5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 20 Aug 2018 15:33:16 -0400
Subject: [PATCH 346/752] No need to load lib/init.js; index.xjs has already
 done this.

---
 web/pages/.examples/000-mail.xjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index dd7dfe4481..c8385dc9e9 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -5,7 +5,6 @@
 if (user.number == 0 || user.alias == settings.guest) exit();
 
 load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + 'forum.js');
 
 var _mpl = getLanguage(settings.language_file || 'english.ini', 'page_mail');
-- 
GitLab


From e3b3092f67c2363efdd63e0e14b69564060b4079 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 20 Aug 2018 16:22:06 -0400
Subject: [PATCH 347/752] If mail is html then do html things, else do the
 regular thing mkay yesno?

---
 web/lib/forum.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 2dad427b9f..bc438214ca 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -356,7 +356,11 @@ function getMailBody(number) {
 
     var decoded = mimeDecode(header, body, 'mail');
     ret.type = decoded.type;
-    ret.body = formatMessage(decoded.body);
+    if (ret.type == 'html') {
+      ret.body = html_decode(decoded.body);
+    } else {
+      ret.body = formatMessage(decoded.body);
+    }
     ret.inlines = decoded.inlines;
     ret.attachments = decoded.attachments;
 
-- 
GitLab


From d2b9cb27aef5e3476ab38fefa53282d582aeae2f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 20 Aug 2018 19:04:23 -0400
Subject: [PATCH 348/752] Comment out previous change.  I don't want to delve
 into this right now.

---
 web/lib/forum.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index bc438214ca..404b4a1e75 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -356,11 +356,11 @@ function getMailBody(number) {
 
     var decoded = mimeDecode(header, body, 'mail');
     ret.type = decoded.type;
-    if (ret.type == 'html') {
-      ret.body = html_decode(decoded.body);
-    } else {
+//    if (ret.type == 'html') {
+//      ret.body = html_decode(decoded.body);
+//    } else {
       ret.body = formatMessage(decoded.body);
-    }
+//    }
     ret.inlines = decoded.inlines;
     ret.attachments = decoded.attachments;
 
-- 
GitLab


From 5c36ada599ec7c61e3391f87759b983ec014139a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 30 Aug 2018 10:33:44 -0400
Subject: [PATCH 349/752] Hide the entire nodelist if ain't nobody online. Only
 show nodes what gots somebody on them. Don't show node numbers, show
 connection methods. Is less cheesy yesno? You'll wanna copy
 web/sidebar/.examples/001-nodelist.xjs up to web/sidebar/001-nodelist.xjs
 after pulling down this update.

---
 web/lib/language/english.ini           | 21 ++++++-
 web/root/api/system.ssjs               | 22 ++++---
 web/sidebar/.examples/001-nodelist.xjs | 87 ++++++++++++++------------
 3 files changed, 79 insertions(+), 51 deletions(-)

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
index 41b7543108..ac57971728 100644
--- a/web/lib/language/english.ini
+++ b/web/lib/language/english.ini
@@ -64,9 +64,28 @@ stat_suffix_files = files
 stat_suffix_directory = directory
 stat_suffix_directories = directories
 
+[page_forum]
+title = XJS Test Forum
+label_thread_from = By
+label_message_from = From
+label_message_to = To
+label_message_date = on
+label_message_subject = Subject
+label_thread_latest_reply = Latest reply by
+badge_poll = POLL
+badge_downvotes = Downvotes - Parent / Thread Total
+badge_upvotes = Upvotes - Parent / Thread Total
+button_post_message = Post a new message
+button_post_poll = Post a new poll
+button_scan_new = Include in new message scan
+button_scan_you = Include in new message scan (messages to you only)
+button_scan_off = Exclude from new message scan
+button_load_more = Load more
+message_loading_threads = Loading threads ...
+
 [sidebar_node_list]
 label_title = Who's Online
-label_node_column = Node
+label_node_column = Via
 label_send_telegram = Send a telegram
 label_status_column = Status
 
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 34a888720e..29e7f078e9 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -19,15 +19,18 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	switch (http_request.query.call[0]) {
 
 		case 'node-list':
-      var usr = new User(0);
-			reply = system.node_list.map(function (node) {
-        usr.number = node.useron;
-				return ({
-          status : format(NodeStatus[node.status], node.aux, node.extaux),
-					action : format(NodeAction[node.action], node.aux, node.extaux),
-          user : (node.status == 3 ? usr.alias : '')
+      var usr = new User(1);
+			reply = system.node_list.reduce(function (a, c) {
+        if (c.status !== 3) return a;
+        usr.number = c.useron;
+				a.push({
+          status : format(NodeStatus[c.status], c.aux, c.extaux),
+					action : format(NodeAction[c.action], c.aux, c.extaux),
+          user : usr.alias,
+          connection : usr.connection
 				});
-			});
+        return a;
+			}, []);
 			for (var un = 1; un < system.lastuser; un++) {
 				usr.number = un;
 				if (usr.connection !== 'HTTP') continue;
@@ -39,7 +42,8 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				reply.push({
           status : '',
 					action : _language.nodelist_action_prefix + ' ' + webAction,
-					user : usr.alias
+					user : usr.alias,
+          connection : 'W'
 				});
 			}
       usr = undefined;
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index fe5fb571e0..9e3e4b35b0 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -2,50 +2,55 @@
 
 <div id="sbbs-nodelist"></div>
 <script type="text/javascript">
-var nodeList = function() {
+var nodeList = function () {
 	$.getJSON(
 		'./api/system.ssjs?call=node-list',
 		function (data) {
-			$('#sbbs-nodelist').html(
-				"<h4><?xjs write(_nll.label_title); ?></h4>" +
-				'<table id="sbbs-nodelist-table" ' +
-				'class="table table-condensed table-responsive table-striped">'+
-				'<thead><tr><th><?xjs write(_nll.label_node_column); ?></th><th><?xjs write(_nll.label_status_column); ?></th></tr></thead>' +
-				'<tbody></tbody>' +
-				'</table>'
-			);
-			data.forEach(
-				function (n, index) {
-					var nll = <?xjs write(system.node_list.length); ?>;
-					$('#sbbs-nodelist-table').append(
-						'<tr>' +
-						'<th scope="row">' +
-						(index >= nll ? "W" : (index + 1)) +
-						'</th>' +
-						'<td id="nodelist-' + index + '">' +
-						(	n.user == ''
-							? n.status
-							: ('<strong>' + n.user + '</strong> ' + n.action)
-						) +
-						'</td>' +
-						'</tr>'
-					);
-					if (n.user != '' &&
-						<?xjs write(user.alias!=settings.guest); ?>
-					) {
-						$('#nodelist-' + index).attr(
-							'title',
-							'<?xjs write(_nll.label_send_telegram); ?>'
-						);
-						$('#nodelist-' + index).css('cursor', 'pointer');
-						$('#nodelist-' + index).click(
-							function () {
-								sendTelegram(n.user);
-							}
-						);
-					}
-				}
-			);
+      if (!data.length) {
+        $('#sbbs-nodelist').parent().addClass('hidden');
+      } else {
+        $('#sbbs-nodelist').parent().removeClass('hidden');
+  			$('#sbbs-nodelist').html(
+  				"<h4><?xjs write(_nll.label_title); ?></h4>" +
+  				'<table id="sbbs-nodelist-table" ' +
+  				'class="table table-condensed table-responsive table-striped">'+
+  				'<thead><tr><th><?xjs write(_nll.label_node_column); ?></th><th><?xjs write(_nll.label_status_column); ?></th></tr></thead>' +
+  				'<tbody></tbody>' +
+  				'</table>'
+  			);
+  			data.forEach(
+  				function (n, index) {
+  					var nll = <?xjs write(system.node_list.length); ?>;
+  					$('#sbbs-nodelist-table').append(
+  						'<tr>' +
+  						'<th scope="row">' +
+  						n.connection + //(index >= nll ? "W" : (index + 1)) +
+  						'</th>' +
+  						'<td id="nodelist-' + index + '">' +
+  						(	n.user == ''
+  							? n.status
+  							: ('<strong>' + n.user + '</strong> ' + n.action)
+  						) +
+  						'</td>' +
+  						'</tr>'
+  					);
+  					if (n.user != '' &&
+  						<?xjs write(user.alias!=settings.guest); ?>
+  					) {
+  						$('#nodelist-' + index).attr(
+  							'title',
+  							'<?xjs write(_nll.label_send_telegram); ?>'
+  						);
+  						$('#nodelist-' + index).css('cursor', 'pointer');
+  						$('#nodelist-' + index).click(
+  							function () {
+  								sendTelegram(n.user);
+  							}
+  						);
+  					}
+  				}
+  			);
+      }
 		}
 	);
 }
-- 
GitLab


From d120c51db26c7f0130ee3ca6c6ede9eddbe857ad Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 00:10:21 -0400
Subject: [PATCH 350/752] Include node number in node object array element.

---
 web/root/api/system.ssjs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 29e7f078e9..4625157958 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -20,10 +20,11 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 
 		case 'node-list':
       var usr = new User(1);
-			reply = system.node_list.reduce(function (a, c) {
+			reply = system.node_list.reduce(function (a, c, i) {
         if (c.status !== 3) return a;
         usr.number = c.useron;
 				a.push({
+          node : i,
           status : format(NodeStatus[c.status], c.aux, c.extaux),
 					action : format(NodeAction[c.action], c.aux, c.extaux),
           user : usr.alias,
-- 
GitLab


From 3d777497502069c20ba458814ce75eff430dec23 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 00:10:49 -0400
Subject: [PATCH 351/752] node column and connection column entries.

---
 web/lib/language/english.ini | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
index ac57971728..0a9ab4fd19 100644
--- a/web/lib/language/english.ini
+++ b/web/lib/language/english.ini
@@ -85,7 +85,8 @@ message_loading_threads = Loading threads ...
 
 [sidebar_node_list]
 label_title = Who's Online
-label_node_column = Via
+label_connection_column = Via
+label_node_column = Node
 label_send_telegram = Send a telegram
 label_status_column = Status
 
-- 
GitLab


From 2c9cda3abb89eee3af55a04d6dfeab5b1e09be60 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 00:11:18 -0400
Subject: [PATCH 352/752] Use the 'active_node_list' setting to decide between
 listing only active nodes vs. only nodes in use.

---
 web/sidebar/.examples/001-nodelist.xjs | 103 ++++++++++++++-----------
 1 file changed, 56 insertions(+), 47 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 9e3e4b35b0..a031d12bca 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -1,59 +1,68 @@
 <?xjs var _nll = getLanguage(settings.language_file || 'english.ini', 'sidebar_node_list'); ?>
 
 <div id="sbbs-nodelist"></div>
+
 <script type="text/javascript">
-var nodeList = function () {
-	$.getJSON(
-		'./api/system.ssjs?call=node-list',
-		function (data) {
-      if (!data.length) {
+
+  const anl = <?xjs write(settings.active_node_list); ?>;
+  const nch = '<?xjs write(settings.active_node_list ? _nll.label_connection_column : _nll.label_node_column) ?>';
+  const nll = <?xjs write(system.node_list.length); ?>;
+
+  function _sb_list_node(e) {
+    $('#sbbs-nodelist-table').append(
+      '<tr>' +
+        '<th scope="row">' + (anl ? e.connection : (e.node + 1)) + '</th>' +
+        '<td id="nodelist-' + e.node + '">' +
+          (	e.user == ''
+            ? e.status
+            : ('<strong>' + e.user + '</strong> ' + e.action)
+          ) +
+        '</td>' +
+      '</tr>'
+    );
+    if (e.user != '' && <?xjs write(user.alias != settings.guest); ?>) {
+      $('#nodelist-' + e.node).attr(
+        'title', '<?xjs write(_nll.label_send_telegram); ?>'
+      );
+      $('#nodelist-' + e.node).css('cursor', 'pointer');
+      $('#nodelist-' + e.node).click(function () { sendTelegram(e.user); });
+    }
+  }
+
+  function _sb_nodelist() {
+  	$.getJSON('./api/system.ssjs?call=node-list', function (data) {
+      if (anl && !data.node_list.length) {
         $('#sbbs-nodelist').parent().addClass('hidden');
       } else {
-        $('#sbbs-nodelist').parent().removeClass('hidden');
+        if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
   			$('#sbbs-nodelist').html(
   				"<h4><?xjs write(_nll.label_title); ?></h4>" +
-  				'<table id="sbbs-nodelist-table" ' +
-  				'class="table table-condensed table-responsive table-striped">'+
-  				'<thead><tr><th><?xjs write(_nll.label_node_column); ?></th><th><?xjs write(_nll.label_status_column); ?></th></tr></thead>' +
-  				'<tbody></tbody>' +
+  				'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
+    				'<thead><tr>' +
+              '<th>' + nch + '</th>' +
+              '<th><?xjs write(_nll.label_status_column); ?></th>' +
+            '</tr></thead>' +
+  				  '<tbody></tbody>' +
   				'</table>'
   			);
-  			data.forEach(
-  				function (n, index) {
-  					var nll = <?xjs write(system.node_list.length); ?>;
-  					$('#sbbs-nodelist-table').append(
-  						'<tr>' +
-  						'<th scope="row">' +
-  						n.connection + //(index >= nll ? "W" : (index + 1)) +
-  						'</th>' +
-  						'<td id="nodelist-' + index + '">' +
-  						(	n.user == ''
-  							? n.status
-  							: ('<strong>' + n.user + '</strong> ' + n.action)
-  						) +
-  						'</td>' +
-  						'</tr>'
-  					);
-  					if (n.user != '' &&
-  						<?xjs write(user.alias!=settings.guest); ?>
-  					) {
-  						$('#nodelist-' + index).attr(
-  							'title',
-  							'<?xjs write(_nll.label_send_telegram); ?>'
-  						);
-  						$('#nodelist-' + index).css('cursor', 'pointer');
-  						$('#nodelist-' + index).click(
-  							function () {
-  								sendTelegram(n.user);
-  							}
-  						);
-  					}
-  				}
-  			);
+        if (!anl) {
+          const nodes = Array(nll);
+          data.node_list.forEach(function (e) { nodes[e.node] = e; });
+          for (var n = 0; n < nll; n++) {
+            if (!nodes[n]) {
+              _sb_list_node({ node: n, user: '', status :'Waiting for call' });
+            } else {
+              _sb_list_node(nodes[n], n);
+            }
+          }
+        } else {
+          data.node_list.forEach(_sb_list_node);
+        }
       }
-		}
-	);
-}
-nodeList();
-setInterval(nodeList, <?xjs write(settings.refresh_interval || 60000); ?>);
+  	});
+  }
+
+  _sb_nodelist();
+  setInterval(_sb_nodelist, <?xjs write(settings.refresh_interval || 60000); ?>);
+
 </script>
-- 
GitLab


From 09ca1647a29c2d65cc67036437cbf8c7a9e22cc5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 00:13:10 -0400
Subject: [PATCH 353/752] Default active_node_list to true. Some ... pathing
 change I don't remember making and not sure if it matters.

---
 web/lib/init.js | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index 37fa1848ce..a3c483da55 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,3 +1,7 @@
+load('modopts.js');
+
+var settings = get_mod_options('web');
+
 // Paths
 settings.web_directory = fullpath(
 	backslash(
@@ -14,8 +18,9 @@ settings.web_root = fullpath(
 	)
 );
 settings.web_lib = backslash(settings.web_directory + 'lib/');
-settings.web_pages = fullpath(backslash(settings.web_directory + 'pages'));
-settings.web_sidebar = fullpath(backslash(settings.web_directory + 'sidebar'));
+
+settings.web_pages = backslash(fullpath(settings.web_root + '../pages'));
+settings.web_sidebar = backslash(fullpath(settings.web_root + '../sidebar'));
 
 // Guest
 if (typeof settings.guest === 'undefined') settings.guest = 'Guest';
@@ -54,3 +59,7 @@ if (typeof settings.page_size !== 'number' || settings.page_size < 1) {
 if (typeof settings.forum_extended_ascii !== 'boolean') {
 	settings.forum_extended_ascii = true;
 }
+
+if (typeof settings.active_node_list !== 'boolean') {
+  settings.active_node_list = true;
+}
-- 
GitLab


From 492679b9f6590be412f29c8ad9bfff1705734739 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 11:05:23 -0400
Subject: [PATCH 354/752] Show nodes in use / available in system stats, and
 update there when new nodelist data is received.

---
 web/sidebar/.examples/001-nodelist.xjs    | 10 +++++++---
 web/sidebar/.examples/003-systemStats.xjs | 23 +++++++++++++++++------
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index a031d12bca..b3df5e9236 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -31,7 +31,7 @@
 
   function _sb_nodelist() {
   	$.getJSON('./api/system.ssjs?call=node-list', function (data) {
-      if (anl && !data.node_list.length) {
+      if (anl && !data.length) {
         $('#sbbs-nodelist').parent().addClass('hidden');
       } else {
         if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
@@ -47,7 +47,7 @@
   			);
         if (!anl) {
           const nodes = Array(nll);
-          data.node_list.forEach(function (e) { nodes[e.node] = e; });
+          data.forEach(function (e) { nodes[e.node] = e; });
           for (var n = 0; n < nll; n++) {
             if (!nodes[n]) {
               _sb_list_node({ node: n, user: '', status :'Waiting for call' });
@@ -56,8 +56,12 @@
             }
           }
         } else {
-          data.node_list.forEach(_sb_list_node);
+          data.forEach(_sb_list_node);
         }
+        // Spans with these classes are used in the 'system stats' sidebar module.
+        // Update the nodes in use / available counters there.
+        $('#sidebar').find('.sb-nodes-in-use').text(data.length);
+        $('#sidebar').find('.sb-nodes-available').text(nll - data.length);
       }
   	});
   }
diff --git a/web/sidebar/.examples/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
index 59dfec2e28..71d1f5e67b 100644
--- a/web/sidebar/.examples/003-systemStats.xjs
+++ b/web/sidebar/.examples/003-systemStats.xjs
@@ -1,5 +1,11 @@
 <?xjs var _sill = getLanguage(settings.language_file || 'english.ini', 'sidebar_system_info'); ?>
 
+<?xjs
+  const _sb_niu = system.node_list.reduce(function (a, c) {
+    return (c.status == 3 ? a + 1 : a);
+  }, 0);
+?>
+
 <h4>System Info</h4>
 <table class="table table-condensed table-responsive table-striped">
 	<tbody>
@@ -17,7 +23,12 @@
 		</tr>
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_nodes); ?></th>
-			<td><?xjs write(system.nodes); ?></td>
+      <td>
+        <?xjs write(system.nodes); ?>
+        (<span title="In use" class="text-danger sb-nodes-in-use"><?xjs write(_sb_niu); ?></span>
+         /
+         <span title="Available" class="text-success sb-nodes-available"><?xjs write(system.nodes - _sb_niu); ?></span>)
+      </td>
 		</tr>
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_uptime); ?></th>
@@ -38,20 +49,20 @@
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_files_uploaded_today); ?></th>
 			<td>
-				<?xjs write(system.stats.files_uploaded_today); ?> 
+				<?xjs write(system.stats.files_uploaded_today); ?>
 				<?xjs write(_sill.stat_suffix_files); ?>
 				<br>
-				(<?xjs write(system.stats.bytes_uploaded_today); ?> 
+				(<?xjs write(system.stats.bytes_uploaded_today); ?>
 				<?xjs write(_sill.stat_suffix_bytes); ?>)
 			</td>
 		</tr>
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_files_downloaded_today); ?></th>
 			<td>
-				<?xjs write(system.stats.files_downloaded_today); ?> 
+				<?xjs write(system.stats.files_downloaded_today); ?>
 				<?xjs write(_sill.stat_suffix_files); ?>
 				<br>
-				(<?xjs write(system.stats.bytes_downloaded_today); ?> 
+				(<?xjs write(system.stats.bytes_downloaded_today); ?>
 				<?xjs write(_sill.stat_suffix_bytes); ?>)
 			</td>
 		</tr>
@@ -64,4 +75,4 @@
 			<td><?xjs write(system.stats.messages_posted_today); ?></td>
 		</tr>
 	</tbody>
-</table>
\ No newline at end of file
+</table>
-- 
GitLab


From b0aa420359de51c64a0fcb2cca14cd70ba870d46 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 11:10:14 -0400
Subject: [PATCH 355/752] Web sessions don't got no node number.

---
 web/sidebar/.examples/001-nodelist.xjs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index b3df5e9236..3387880619 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -47,7 +47,13 @@
   			);
         if (!anl) {
           const nodes = Array(nll);
-          data.forEach(function (e) { nodes[e.node] = e; });
+          data.forEach(function (e) {
+            if (typeof e.node == 'number') {
+              nodes[e.node] = e;
+            } else {
+              nodes.push(e); // Web user
+            }
+          });
           for (var n = 0; n < nll; n++) {
             if (!nodes[n]) {
               _sb_list_node({ node: n, user: '', status :'Waiting for call' });
-- 
GitLab


From 795a73f1094abadf1efe802d5aa99a695ece7b07 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 11:19:53 -0400
Subject: [PATCH 356/752] Some more handling of terminal vs web sessions.

---
 web/sidebar/.examples/001-nodelist.xjs | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 3387880619..96ae408bdc 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -7,16 +7,16 @@
   const anl = <?xjs write(settings.active_node_list); ?>;
   const nch = '<?xjs write(settings.active_node_list ? _nll.label_connection_column : _nll.label_node_column) ?>';
   const nll = <?xjs write(system.node_list.length); ?>;
+  var niu = 0;
 
   function _sb_list_node(e) {
     $('#sbbs-nodelist-table').append(
       '<tr>' +
-        '<th scope="row">' + (anl ? e.connection : (e.node + 1)) + '</th>' +
+        '<th scope="row">' +
+          (anl ? e.connection : (typeof e.node == 'number' ? e.node + 1 : e.connection)) +
+        '</th>' +
         '<td id="nodelist-' + e.node + '">' +
-          (	e.user == ''
-            ? e.status
-            : ('<strong>' + e.user + '</strong> ' + e.action)
-          ) +
+          (e.user == '' ? e.status : ('<strong>' + e.user + '</strong> ' + e.action)) +
         '</td>' +
       '</tr>'
     );
@@ -27,6 +27,7 @@
       $('#nodelist-' + e.node).css('cursor', 'pointer');
       $('#nodelist-' + e.node).click(function () { sendTelegram(e.user); });
     }
+    if (typeof e.node == 'number' && e.user != '') niu++;
   }
 
   function _sb_nodelist() {
@@ -45,6 +46,7 @@
   				  '<tbody></tbody>' +
   				'</table>'
   			);
+        niu = 0;
         if (!anl) {
           const nodes = Array(nll);
           data.forEach(function (e) {
@@ -54,7 +56,7 @@
               nodes.push(e); // Web user
             }
           });
-          for (var n = 0; n < nll; n++) {
+          for (var n = 0; n < nodes.length; n++) {
             if (!nodes[n]) {
               _sb_list_node({ node: n, user: '', status :'Waiting for call' });
             } else {
@@ -66,8 +68,8 @@
         }
         // Spans with these classes are used in the 'system stats' sidebar module.
         // Update the nodes in use / available counters there.
-        $('#sidebar').find('.sb-nodes-in-use').text(data.length);
-        $('#sidebar').find('.sb-nodes-available').text(nll - data.length);
+        $('#sidebar').find('.sb-nodes-in-use').text(niu);
+        $('#sidebar').find('.sb-nodes-available').text(nll - niu);
       }
   	});
   }
-- 
GitLab


From 288c031f59ea5c0dd26bb6941ce3cf54134f5c1b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 11:54:55 -0400
Subject: [PATCH 357/752] If modopts->[web]->hide_empty_stats is false, show
 all stats including lame zero values. Otherwise, only show stats with
 non-zero values. Set settings.hide_empty_stats to true if it is undefined in
 modopts.

---
 web/lib/init.js                           |  4 +
 web/sidebar/.examples/003-systemStats.xjs | 97 ++++++++++++++---------
 2 files changed, 65 insertions(+), 36 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index a3c483da55..238026890b 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -63,3 +63,7 @@ if (typeof settings.forum_extended_ascii !== 'boolean') {
 if (typeof settings.active_node_list !== 'boolean') {
   settings.active_node_list = true;
 }
+
+if (typeof settings.hide_empty_stats !== 'boolean') {
+  settings.hide_empty_stats = true;
+}
diff --git a/web/sidebar/.examples/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
index 71d1f5e67b..83f9813fcb 100644
--- a/web/sidebar/.examples/003-systemStats.xjs
+++ b/web/sidebar/.examples/003-systemStats.xjs
@@ -9,18 +9,22 @@
 <h4>System Info</h4>
 <table class="table table-condensed table-responsive table-striped">
 	<tbody>
+
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_sysop); ?></th>
 			<td><?xjs write(system.operator); ?></td>
 		</tr>
+
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_location); ?></th>
 			<td><?xjs write(system.location); ?></td>
 		</tr>
+
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_users); ?></th>
 			<td><?xjs write(system.stats.total_users); ?></td>
 		</tr>
+
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_nodes); ?></th>
       <td>
@@ -30,49 +34,70 @@
          <span title="Available" class="text-success sb-nodes-available"><?xjs write(system.nodes - _sb_niu); ?></span>)
       </td>
 		</tr>
+
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_uptime); ?></th>
 			<td><?xjs write(system.secondstr(time() - system.uptime)); ?></td>
 		</tr>
+
 		<tr>
 			<th scope="row"><?xjs write(_sill.label_calls_total); ?></th>
 			<td><?xjs write(system.stats.total_logons); ?></td>
 		</tr>
-		<tr>
-			<th scope="row"><?xjs write(_sill.label_calls_today); ?></th>
-			<td><?xjs write(system.stats.logons_today); ?></td>
-		</tr>
-		<tr>
-			<th scope="row"><?xjs write(_sill.label_files_total); ?></th>
-			<td><?xjs write(system.stats.total_files); ?></td>
-		</tr>
-		<tr>
-			<th scope="row"><?xjs write(_sill.label_files_uploaded_today); ?></th>
-			<td>
-				<?xjs write(system.stats.files_uploaded_today); ?>
-				<?xjs write(_sill.stat_suffix_files); ?>
-				<br>
-				(<?xjs write(system.stats.bytes_uploaded_today); ?>
-				<?xjs write(_sill.stat_suffix_bytes); ?>)
-			</td>
-		</tr>
-		<tr>
-			<th scope="row"><?xjs write(_sill.label_files_downloaded_today); ?></th>
-			<td>
-				<?xjs write(system.stats.files_downloaded_today); ?>
-				<?xjs write(_sill.stat_suffix_files); ?>
-				<br>
-				(<?xjs write(system.stats.bytes_downloaded_today); ?>
-				<?xjs write(_sill.stat_suffix_bytes); ?>)
-			</td>
-		</tr>
-		<tr>
-			<th scope="row"><?xjs write(_sill.label_messages_total); ?></th>
-			<td><?xjs write(system.stats.total_messages); ?></td>
-		</tr>
-		<tr>
-			<th scope="row"><?xjs write(_sill.label_messages_posted_today); ?></th>
-			<td><?xjs write(system.stats.messages_posted_today); ?></td>
-		</tr>
+
+    <?xjs if (!settings.hide_empty_stats || system.stats.logons_today) { ?>
+  		<tr>
+  			<th scope="row"><?xjs write(_sill.label_calls_today); ?></th>
+  			<td><?xjs write(system.stats.logons_today); ?></td>
+  		</tr>
+    <?xjs } ?>
+
+    <?xjs if (!settings.hide_empty_stats || system.stats.total_files) { ?>
+  		<tr>
+  			<th scope="row"><?xjs write(_sill.label_files_total); ?></th>
+  			<td><?xjs write(system.stats.total_files); ?></td>
+  		</tr>
+    <?xjs } ?>
+
+    <?xjs if (!settings.hide_empty_stats || system.stats.files_uploaded_today) { ?>
+  		<tr>
+  			<th scope="row"><?xjs write(_sill.label_files_uploaded_today); ?></th>
+  			<td>
+  				<?xjs write(system.stats.files_uploaded_today); ?>
+  				<?xjs write(_sill.stat_suffix_files); ?>
+  				<br>
+  				(<?xjs write(system.stats.bytes_uploaded_today); ?>
+  				<?xjs write(_sill.stat_suffix_bytes); ?>)
+  			</td>
+  		</tr>
+    <?xjs } ?>
+
+    <?xjs if (!settings.hide_empty_stats || system.stats.files_downloaded_today) { ?>
+  		<tr>
+  			<th scope="row"><?xjs write(_sill.label_files_downloaded_today); ?></th>
+  			<td>
+  				<?xjs write(system.stats.files_downloaded_today); ?>
+  				<?xjs write(_sill.stat_suffix_files); ?>
+  				<br>
+  				(<?xjs write(system.stats.bytes_downloaded_today); ?>
+  				<?xjs write(_sill.stat_suffix_bytes); ?>)
+  			</td>
+  		</tr>
+    <?xjs } ?>
+
+    <?xjs if (!settings.hide_empty_stats || system.stats.total_messages) { ?>
+  		<tr>
+  			<th scope="row"><?xjs write(_sill.label_messages_total); ?></th>
+  			<td><?xjs write(system.stats.total_messages); ?></td>
+  		</tr>
+    <?xjs } ?>
+
+    <?xjs if (!settings.hide_empty_stats || system.stats.messages_posted_today) { ?>
+  		<tr>
+  			<th scope="row"><?xjs write(_sill.label_messages_posted_today); ?></th>
+  			<td><?xjs write(system.stats.messages_posted_today); ?></td>
+  		</tr>
+    <?xjs } ?>
+
 	</tbody>
 </table>
-- 
GitLab


From 883f6001bde669390411e0f924a8e3f44a49f675 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 31 Aug 2018 12:51:01 -0400
Subject: [PATCH 358/752] Change how settings are validated and defaults are
 applied.

---
 web/lib/init.js | 87 ++++++++++++++++++++++++-------------------------
 1 file changed, 43 insertions(+), 44 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index 238026890b..423e1e402a 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,3 +1,4 @@
+load('sbbsdefs.js');
 load('modopts.js');
 
 var settings = get_mod_options('web');
@@ -22,48 +23,46 @@ settings.web_lib = backslash(settings.web_directory + 'lib/');
 settings.web_pages = backslash(fullpath(settings.web_root + '../pages'));
 settings.web_sidebar = backslash(fullpath(settings.web_root + '../sidebar'));
 
-// Guest
-if (typeof settings.guest === 'undefined') settings.guest = 'Guest';
-if (system.matchuser(settings.guest) == 0) exit();
+var defaults = {
+  guest : {
+    default : 'Guest',
+    test : function () {
+      return system.matchuser(settings.guest) ? null : 'Guest account unavailable';
+    }
+  },
+  timeout : { default : 43200 },
+  user_registration : { default : false },
+  minimum_password_length : { default : 4 },
+  email_validation : { default : true },
+  email_validation_level : { default : 50 },
+  max_messages : {
+    default : 0,
+    test : function () {
+      return settings.max_messages >= 0 ? null : 'max_messages must be >= 0';
+    }
+  },
+  page_size : {
+    default : 25,
+    test : function () {
+      return settings.page_size >= 1 ? null : 'page_size must be >= 1';
+    }
+  },
+  forum_extended_ascii : { default : true },
+  active_node_list : { default : true },
+  hide_empty_stats : { default : true }
+};
 
-// Timeout
-if (typeof settings.timeout !== 'number') settings.timeout = 43200;
-
-// Registration
-if (typeof settings.user_registration !== 'boolean') {
-	settings.user_registration = false;
-} else {
-
-	if (typeof settings.minimum_password_length !== 'number') {
-		settings.minimum_password_length = 4;
-	}
-
-	if (typeof settings.email_validation !== 'boolean') {
-		settings.email_validation = true;
-	}
-
-	if (typeof settings.email_validation_level !== 'number') {
-		settings.email_validation_level = 50;
-	}
-
-}
-
-if (typeof settings.max_messages !== 'number' || settings.max_messages < 0) {
-	settings.max_messages = 0;
-}
-
-if (typeof settings.page_size !== 'number' || settings.page_size < 1) {
-	settings.page_size = 25;
-}
-
-if (typeof settings.forum_extended_ascii !== 'boolean') {
-	settings.forum_extended_ascii = true;
-}
-
-if (typeof settings.active_node_list !== 'boolean') {
-  settings.active_node_list = true;
-}
-
-if (typeof settings.hide_empty_stats !== 'boolean') {
-  settings.hide_empty_stats = true;
-}
+Object.keys(defaults).forEach(function (e) {
+  if (typeof settings[e] == 'undefined') {
+    settings[e] = defaults[e].default;
+  } else if (typeof settings[e] != typeof defaults[e].default) {
+    log(LOG_ERROR, 'Invalid ' + e + ' setting: ' + settings[e]);
+    exit();
+  } else if (typeof defaults[e].test == 'function') {
+    const t = defaults[e].test();
+    if (t !== null) {
+      log(LOG_ERROR, t);
+      exit();
+    }
+  }
+});
-- 
GitLab


From 0f63b23f2d59f095059d93780e4a22256e3b11e3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 3 Oct 2018 11:19:20 -0400
Subject: [PATCH 359/752] If on an MS-DOS computer, run pkunzip.exe from the
 system exec directory, elsewise just make assumtion that pkunzip.zip is on
 the PATH somewheres, and then unzip it, and then run it. Most of this commit
 message is wildly inaccurate.

---
 mods/webv4-installer.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 3612e0cfb8..c35a166745 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -25,9 +25,10 @@ function download(url, target) {
 
 function extract(file, target) {
 	if (!file_isdir(install_dir)) {
-        if (!mkdir(install_dir)) return false;
-    }
-	return system.exec('unzip -uqo ' + file + ' -d ' + target) == 0;
+    if (!mkdir(install_dir)) return false;
+  }
+  const zp = system.platform.search(/^win/i) > -1 ? system.exec_dir : '';
+	return system.exec(zp + 'unzip -uqo ' + file + ' -d ' + target) == 0;
 }
 
 function update_sbbs_ini(root_directory, error_directory) {
-- 
GitLab


From e8f8ec1d8e11066fd09fc348c2ea3d3e79b1abae Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 2 Jan 2019 20:45:53 -0800
Subject: [PATCH 360/752] Use file_size_str() from load/file_size.js to display
 file stats for more human-readable/pretty appearance.

---
 web/sidebar/.examples/003-systemStats.xjs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/web/sidebar/.examples/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
index 83f9813fcb..394b93555e 100644
--- a/web/sidebar/.examples/003-systemStats.xjs
+++ b/web/sidebar/.examples/003-systemStats.xjs
@@ -4,6 +4,7 @@
   const _sb_niu = system.node_list.reduce(function (a, c) {
     return (c.status == 3 ? a + 1 : a);
   }, 0);
+  require('file_size.js', 'file_size_str');
 ?>
 
 <h4>System Info</h4>
@@ -66,7 +67,7 @@
   				<?xjs write(system.stats.files_uploaded_today); ?>
   				<?xjs write(_sill.stat_suffix_files); ?>
   				<br>
-  				(<?xjs write(system.stats.bytes_uploaded_today); ?>
+  				(<?xjs write(file_size_str(system.stats.bytes_uploaded_today)); ?>
   				<?xjs write(_sill.stat_suffix_bytes); ?>)
   			</td>
   		</tr>
@@ -79,7 +80,7 @@
   				<?xjs write(system.stats.files_downloaded_today); ?>
   				<?xjs write(_sill.stat_suffix_files); ?>
   				<br>
-  				(<?xjs write(system.stats.bytes_downloaded_today); ?>
+  				(<?xjs write(file_size_str(system.stats.bytes_downloaded_today)); ?>
   				<?xjs write(_sill.stat_suffix_bytes); ?>)
   			</td>
   		</tr>
-- 
GitLab


From 779e0ddfbe1c5ab0fe926177f0bcc0de39684354 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Jan 2019 10:52:50 -0500
Subject: [PATCH 361/752] Don't invoke FileBase until it's really needed. This
 might speed up library/directory listing. Listing files in a directory may
 still be slow, not sure if anything can be done about that.

---
 web/lib/files.js | 44 +++++++++++++++++++-------------------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/web/lib/files.js b/web/lib/files.js
index 3c6e8793dd..870131c841 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -1,39 +1,33 @@
 load('filebase.js');
 load('file_size.js');
 
+function count_files(dir) {
+    var n = 0;
+    const fn = format("%s%s.ixb", file_area.dir[dir].data_dir, dir);
+    if (!file_exists(fn)) return n;
+    return Math.floor(file_size(fn) / 22);
+}
+
 function listLibraries() {
-	return file_area.lib_list.filter(
-		function (library) {
-            if (library.dir_list.length < 1) return false;
-            var dirs = listDirectories(library.index);
-            return dirs.length > 0;
-        }
-	);
+	return file_area.lib_list.filter(function (library) {
+        return library.dir_list.length >= 1;
+    });
 }
 
 function listDirectories(library) {
 	var dirs = [];
-	file_area.lib_list[library].dir_list.forEach(
-		function (dir) {
-			var fd = new FileBase(dir.code);
-			if (fd.length < 1) return;
-			dirs.push({'dir' : dir, 'fileCount' : fd.length });
-		}
-	);
+	file_area.lib_list[library].dir_list.forEach(function (dir) {
+        const fc = count_files(dir.code);
+        if (fc < 1) return;
+		dirs.push({'dir' : dir, 'fileCount' : fc });
+	});
 	return dirs;
 }
 
 function listFiles(dir) {
 	var fd = new FileBase(file_area.dir[dir].code);
-	var files = fd.map(
-		function (df) {
-			if (typeof df.path !== 'undefined') {
-				df.size = file_size_str(file_size(df.path));
-			} else {
-				df.size = 'Unknown';
-			}
-			return df;
-		}
-	);
-	return files;
+	return fd.map(function (df) {
+        df.size = df.path ? file_size_str(file_size(df.path)) : 'Unknown';
+		return df;
+	});
 }
-- 
GitLab


From 5d51f628b47ffc65c4d692b44259d622003af78d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 4 Jan 2019 13:18:55 -0500
Subject: [PATCH 362/752] Comment re: magic number. No functional change.

---
 web/lib/files.js | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/web/lib/files.js b/web/lib/files.js
index 870131c841..58d9c8aa37 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -5,7 +5,7 @@ function count_files(dir) {
     var n = 0;
     const fn = format("%s%s.ixb", file_area.dir[dir].data_dir, dir);
     if (!file_exists(fn)) return n;
-    return Math.floor(file_size(fn) / 22);
+    return Math.floor(file_size(fn) / 22); // ixb record length is 22 bytes
 }
 
 function listLibraries() {
@@ -19,14 +19,13 @@ function listDirectories(library) {
 	file_area.lib_list[library].dir_list.forEach(function (dir) {
         const fc = count_files(dir.code);
         if (fc < 1) return;
-		dirs.push({'dir' : dir, 'fileCount' : fc });
+		dirs.push({ dir: dir, fileCount: fc });
 	});
 	return dirs;
 }
 
 function listFiles(dir) {
-	var fd = new FileBase(file_area.dir[dir].code);
-	return fd.map(function (df) {
+	return (new FileBase(file_area.dir[dir].code)).map(function (df) {
         df.size = df.path ? file_size_str(file_size(df.path)) : 'Unknown';
 		return df;
 	});
-- 
GitLab


From 67ad9263a689368f137d1ebe70a40dab58f29153 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 01:49:17 -0500
Subject: [PATCH 363/752] Start of a new localization method.

---
 web/lib/init.js          |   2 +
 web/lib/locale.js        |   7 +++
 web/lib/locale/en_ca.ini |   5 ++
 web/lib/locale/en_ca.js  |   9 +++
 web/lib/locale/en_us.ini | 130 +++++++++++++++++++++++++++++++++++++++
 web/lib/locale/en_us.js  |  53 ++++++++++++++++
 6 files changed, 206 insertions(+)
 create mode 100644 web/lib/locale.js
 create mode 100644 web/lib/locale/en_ca.ini
 create mode 100644 web/lib/locale/en_ca.js
 create mode 100644 web/lib/locale/en_us.ini
 create mode 100644 web/lib/locale/en_us.js

diff --git a/web/lib/init.js b/web/lib/init.js
index 423e1e402a..f2f74ce2a1 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -66,3 +66,5 @@ Object.keys(defaults).forEach(function (e) {
     }
   }
 });
+
+load(settings.web_lib + 'locale.js');
diff --git a/web/lib/locale.js b/web/lib/locale.js
new file mode 100644
index 0000000000..0ab1e257e8
--- /dev/null
+++ b/web/lib/locale.js
@@ -0,0 +1,7 @@
+if (!settings.locale) {
+    load(backslash(settings.web_lib + 'locale/') + 'en_us.js');
+} else {
+    load(backslash(settings.web_lib + 'locale/') + settings.locale + '.js');
+}
+
+const locale = new Locale();
diff --git a/web/lib/locale/en_ca.ini b/web/lib/locale/en_ca.ini
new file mode 100644
index 0000000000..fdafd671d7
--- /dev/null
+++ b/web/lib/locale/en_ca.ini
@@ -0,0 +1,5 @@
+[page_register]
+input_zipcode = Postal code
+input_location = Location (City, Province)
+placeholder_zipcode = H0H0H0
+placeholder_location = City, Province
diff --git a/web/lib/locale/en_ca.js b/web/lib/locale/en_ca.js
new file mode 100644
index 0000000000..ef775e2670
--- /dev/null
+++ b/web/lib/locale/en_ca.js
@@ -0,0 +1,9 @@
+load(backslash(settings.web_lib + 'locale/') + 'en_us.js');
+
+function EN_CA() {
+    EN_US.call(this, 'en_ca');
+}
+EN_CA.prototype = Object.create(EN_US.prototype);
+EN_CA.prototype.constructor = EN_US;
+
+var Locale = EN_CA;
diff --git a/web/lib/locale/en_us.ini b/web/lib/locale/en_us.ini
new file mode 100644
index 0000000000..7d20ad6c02
--- /dev/null
+++ b/web/lib/locale/en_us.ini
@@ -0,0 +1,130 @@
+[main]
+button_close = Close
+button_submit = Submit
+button_login = Log in
+input_username = Username
+input_password = Password
+label_sidebar = Sidebar
+label_unread_mail = Unread mail
+menu_item_login = Log in
+menu_item_logout = Log out
+menu_item_mail = Mail
+menu_item_register = Register
+
+[page_home]
+button_ftelnet = Connect via Telnet
+
+[page_mail]
+button_post_new = Post a new message
+button_select_all = Select all messages
+button_delete_selected = Delete selected messages
+label_message_from = From
+label_message_to = To
+label_message_date = on
+label_message_subject = Subject
+label_tab_inbox = Inbox
+label_tab_sent = Sent
+
+[page_register]
+title = Register
+button_register = Register
+label_field_required = Required
+input_alias = Username
+input_password = Password
+input_password_confirm = Confirm password
+input_email = Email address
+input_name = Real name
+input_street_address = Street address
+input_zipcode = Zip Code
+input_location = Location (City, State)
+input_phone = Telephone number
+input_birthdate = Birthdate
+input_gender = Gender
+input_gender_withheld = Withheld
+input_gender_male = Male
+input_gender_female = Female
+input_gender_other = Other
+input_registration_password = Registration password
+stat_suffix_field_required = required
+help_text_required = Fields marked with an asterisk are required.  All others can be left blank if you wish.
+help_text_minimum_characters = Minimum of %d characters
+help_text_maximum_characters = Maximum of %d characters
+message_account_created = Your account has been created
+placeholder_netmail = pat@m.f
+placeholder_name = Pat Androgyne
+placeholder_street_address = 123 Any Street
+placeholder_zipcode = 92860
+placeholder_location = City, State
+placeholder_phone = 800-555-5555
+
+[page_files]
+title = Files
+stat_suffix_file = file
+stat_suffix_files = files
+stat_suffix_directory = directory
+stat_suffix_directories = directories
+
+[page_forum]
+title = XJS Test Forum
+label_thread_from = By
+label_message_from = From
+label_message_to = To
+label_message_date = on
+label_message_subject = Subject
+label_thread_latest_reply = Latest reply by
+badge_poll = POLL
+badge_downvotes = Downvotes - Parent / Thread Total
+badge_upvotes = Upvotes - Parent / Thread Total
+button_post_message = Post a new message
+button_post_poll = Post a new poll
+button_scan_new = Include in new message scan
+button_scan_you = Include in new message scan (messages to you only)
+button_scan_off = Exclude from new message scan
+button_load_more = Load more
+message_loading_threads = Loading threads ...
+
+[sidebar_node_list]
+label_title = Who's Online
+label_connection_column = Via
+label_node_column = Node
+label_send_telegram = Send a telegram
+label_status_column = Status
+
+[sidebar_system_info]
+label_title = System Info
+label_sysop = Sysop:
+label_location = Location:
+label_users = Users:
+label_nodes = Nodes:
+label_uptime = Uptime:
+label_calls_total = Calls:
+label_calls_today = Calls today:
+label_files_total = Files:
+label_files_uploaded_today = U/L today:
+label_files_downloaded_today = D/L today:
+label_messages_total = Messages:
+label_messages_posted_today = Posted today:
+stat_suffix_files = files
+stat_suffix_bytes = bytes
+
+[api_system]
+nodelist_action_prefix = viewing
+telegram_header_format = Telegram from %s via WWW on %s
+
+[api_register]
+log_success = User #%d registered via HTTP
+log_bot_attempt = Hidden registration form field filled. Likely a bot. Cancelling registration.
+error_failed = Failed to create user record.
+error_bad_syspass = Incorrect registration password.
+error_invalid_alias = Valid username is required.
+error_alias_taken = Username already taken.
+error_password_mismatch = Password and confirmation are required, and must match.
+error_password_length = Password must be %d to %d characters in length.
+error_email_required = Valid email address is required.
+error_invalid_email = Invalid email address
+error_invalid_name = Valid real name is required.
+error_invalid_location = Valid location is required.
+error_invalid_street_address = Valid street address and postal code are required
+error_invalid_phone = Valid phone number is required.
+error_invalid_gender = Sex is required. Heh heh heh.
+error_invalid_birthdate = Valid birthdate is required.
diff --git a/web/lib/locale/en_us.js b/web/lib/locale/en_us.js
new file mode 100644
index 0000000000..fb94f98d7b
--- /dev/null
+++ b/web/lib/locale/en_us.js
@@ -0,0 +1,53 @@
+function EN_US(name) {
+
+    const ini_path = backslash(settings.web_lib + 'locale/') + 'en_us.ini';
+    if (typeof name == 'string') {
+        const mod_path = backslash(settings.web_lib + 'locale/') + name + '.ini';
+    }
+
+    var f = new File(ini_path);
+    f.open('r');
+    var sections = f.iniGetSections();
+    f.close();
+    f = undefined;
+
+    const strings = {};
+    sections.forEach(function (e) {
+        Object.defineProperty(strings, e, {
+            enumerable: true,
+            get: function () {
+                var f = new File(ini_path);
+                f.open('r');
+                const o = f.iniGetObject(e);
+                f.close();
+                if (mod_path) {
+                    f = new File(mod_path);
+                    f.open('r');
+                    const oo = f.iniGetObject(e);
+                    f.close();
+                    if (oo !== null) {
+                        Object.keys(oo).forEach(function (ee) {
+                            o[ee] = oo[ee];
+                        });
+                    }
+                }
+                f = undefined;
+                return o;
+            }
+        });
+    });
+    Object.defineProperty(this, 'strings', { value: strings });
+
+}
+
+EN_US.prototype.group_numbers = function (n) {
+    n = n + '';
+    const d = n.indexOf('.');
+    return n.substring(0, d > -1 ? d : n.length).split('').reverse().reduce(
+        function (a, c, i) {
+            return a + (i > 0 && !(i % 3) ? ',' + c : c);
+        }, ''
+    ).split('').reverse().join('') + (d > -1 ? n.substring(d) : '');
+}
+
+var Locale = EN_US;
-- 
GitLab


From e5a721a9d28bfc76641787d9fe2e87682cccaf74 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 01:58:28 -0500
Subject: [PATCH 364/752] Use var to avoid redeclaration errors. Should sort
 out load() stuff eventually though.

---
 web/lib/locale.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/locale.js b/web/lib/locale.js
index 0ab1e257e8..17ccb6bd60 100644
--- a/web/lib/locale.js
+++ b/web/lib/locale.js
@@ -4,4 +4,4 @@ if (!settings.locale) {
     load(backslash(settings.web_lib + 'locale/') + settings.locale + '.js');
 }
 
-const locale = new Locale();
+var locale = new Locale();
-- 
GitLab


From 5cef36d6792db2ae60d0a62417a8d9320fe3f7da Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 02:08:41 -0500
Subject: [PATCH 365/752] Use new locale stuff, with number grouping.

---
 web/sidebar/.examples/003-systemStats.xjs | 50 +++++++++++------------
 1 file changed, 24 insertions(+), 26 deletions(-)

diff --git a/web/sidebar/.examples/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
index 394b93555e..dd50b06b94 100644
--- a/web/sidebar/.examples/003-systemStats.xjs
+++ b/web/sidebar/.examples/003-systemStats.xjs
@@ -1,5 +1,3 @@
-<?xjs var _sill = getLanguage(settings.language_file || 'english.ini', 'sidebar_system_info'); ?>
-
 <?xjs
   const _sb_niu = system.node_list.reduce(function (a, c) {
     return (c.status == 3 ? a + 1 : a);
@@ -12,22 +10,22 @@
 	<tbody>
 
 		<tr>
-			<th scope="row"><?xjs write(_sill.label_sysop); ?></th>
+			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_sysop); ?></th>
 			<td><?xjs write(system.operator); ?></td>
 		</tr>
 
 		<tr>
-			<th scope="row"><?xjs write(_sill.label_location); ?></th>
+			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_location); ?></th>
 			<td><?xjs write(system.location); ?></td>
 		</tr>
 
 		<tr>
-			<th scope="row"><?xjs write(_sill.label_users); ?></th>
-			<td><?xjs write(system.stats.total_users); ?></td>
+			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_users); ?></th>
+			<td><?xjs write(locale.group_numbers(system.stats.total_users)); ?></td>
 		</tr>
 
 		<tr>
-			<th scope="row"><?xjs write(_sill.label_nodes); ?></th>
+			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_nodes); ?></th>
       <td>
         <?xjs write(system.nodes); ?>
         (<span title="In use" class="text-danger sb-nodes-in-use"><?xjs write(_sb_niu); ?></span>
@@ -37,66 +35,66 @@
 		</tr>
 
 		<tr>
-			<th scope="row"><?xjs write(_sill.label_uptime); ?></th>
+			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_uptime); ?></th>
 			<td><?xjs write(system.secondstr(time() - system.uptime)); ?></td>
 		</tr>
 
 		<tr>
-			<th scope="row"><?xjs write(_sill.label_calls_total); ?></th>
-			<td><?xjs write(system.stats.total_logons); ?></td>
+			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_calls_total); ?></th>
+			<td><?xjs write(locale.group_numbers(system.stats.total_logons)); ?></td>
 		</tr>
 
     <?xjs if (!settings.hide_empty_stats || system.stats.logons_today) { ?>
   		<tr>
-  			<th scope="row"><?xjs write(_sill.label_calls_today); ?></th>
-  			<td><?xjs write(system.stats.logons_today); ?></td>
+  			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_calls_today); ?></th>
+  			<td><?xjs write(local.group_numbers(system.stats.logons_today)); ?></td>
   		</tr>
     <?xjs } ?>
 
     <?xjs if (!settings.hide_empty_stats || system.stats.total_files) { ?>
   		<tr>
-  			<th scope="row"><?xjs write(_sill.label_files_total); ?></th>
-  			<td><?xjs write(system.stats.total_files); ?></td>
+  			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_files_total); ?></th>
+  			<td><?xjs write(locale.group_numbers(system.stats.total_files)); ?></td>
   		</tr>
     <?xjs } ?>
 
     <?xjs if (!settings.hide_empty_stats || system.stats.files_uploaded_today) { ?>
   		<tr>
-  			<th scope="row"><?xjs write(_sill.label_files_uploaded_today); ?></th>
+  			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_files_uploaded_today); ?></th>
   			<td>
-  				<?xjs write(system.stats.files_uploaded_today); ?>
-  				<?xjs write(_sill.stat_suffix_files); ?>
+  				<?xjs write(locale.group_numbers(system.stats.files_uploaded_today)); ?>
+  				<?xjs write(locale.strings.sidebar_system_info.stat_suffix_files); ?>
   				<br>
   				(<?xjs write(file_size_str(system.stats.bytes_uploaded_today)); ?>
-  				<?xjs write(_sill.stat_suffix_bytes); ?>)
+  				<?xjs write(locale.strings.sidebar_system_info.stat_suffix_bytes); ?>)
   			</td>
   		</tr>
     <?xjs } ?>
 
     <?xjs if (!settings.hide_empty_stats || system.stats.files_downloaded_today) { ?>
   		<tr>
-  			<th scope="row"><?xjs write(_sill.label_files_downloaded_today); ?></th>
+  			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_files_downloaded_today); ?></th>
   			<td>
-  				<?xjs write(system.stats.files_downloaded_today); ?>
-  				<?xjs write(_sill.stat_suffix_files); ?>
+  				<?xjs write(locale.group_numbers(system.stats.files_downloaded_today)); ?>
+  				<?xjs write(locale.strings.sidebar_system_info.stat_suffix_files); ?>
   				<br>
   				(<?xjs write(file_size_str(system.stats.bytes_downloaded_today)); ?>
-  				<?xjs write(_sill.stat_suffix_bytes); ?>)
+  				<?xjs write(locale.strings.sidebar_system_info.stat_suffix_bytes); ?>)
   			</td>
   		</tr>
     <?xjs } ?>
 
     <?xjs if (!settings.hide_empty_stats || system.stats.total_messages) { ?>
   		<tr>
-  			<th scope="row"><?xjs write(_sill.label_messages_total); ?></th>
-  			<td><?xjs write(system.stats.total_messages); ?></td>
+  			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_messages_total); ?></th>
+  			<td><?xjs write(locale.group_numbers(system.stats.total_messages)); ?></td>
   		</tr>
     <?xjs } ?>
 
     <?xjs if (!settings.hide_empty_stats || system.stats.messages_posted_today) { ?>
   		<tr>
-  			<th scope="row"><?xjs write(_sill.label_messages_posted_today); ?></th>
-  			<td><?xjs write(system.stats.messages_posted_today); ?></td>
+  			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_messages_posted_today); ?></th>
+  			<td><?xjs write(locale.group_numbers(system.stats.messages_posted_today)); ?></td>
   		</tr>
     <?xjs } ?>
 
-- 
GitLab


From fe10ed487f6662cb6ee0ccf777c93bece47abec5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 02:17:03 -0500
Subject: [PATCH 366/752] Use new locale stuff.

---
 web/sidebar/.examples/001-nodelist.xjs | 132 ++++++++++++-------------
 1 file changed, 64 insertions(+), 68 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 96ae408bdc..48657734ef 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -1,80 +1,76 @@
-<?xjs var _nll = getLanguage(settings.language_file || 'english.ini', 'sidebar_node_list'); ?>
-
 <div id="sbbs-nodelist"></div>
 
 <script type="text/javascript">
 
-  const anl = <?xjs write(settings.active_node_list); ?>;
-  const nch = '<?xjs write(settings.active_node_list ? _nll.label_connection_column : _nll.label_node_column) ?>';
-  const nll = <?xjs write(system.node_list.length); ?>;
-  var niu = 0;
+    const anl = <?xjs write(settings.active_node_list); ?>;
+    const nch = '<?xjs write(settings.active_node_list ? locale.strings.sidebar_node_list.label_connection_column : locale.strings.sidebar_node_list.label_node_column) ?>';
+    const nll = <?xjs write(system.node_list.length); ?>;
+    var niu = 0;
 
-  function _sb_list_node(e) {
-    $('#sbbs-nodelist-table').append(
-      '<tr>' +
-        '<th scope="row">' +
-          (anl ? e.connection : (typeof e.node == 'number' ? e.node + 1 : e.connection)) +
-        '</th>' +
-        '<td id="nodelist-' + e.node + '">' +
-          (e.user == '' ? e.status : ('<strong>' + e.user + '</strong> ' + e.action)) +
-        '</td>' +
-      '</tr>'
-    );
-    if (e.user != '' && <?xjs write(user.alias != settings.guest); ?>) {
-      $('#nodelist-' + e.node).attr(
-        'title', '<?xjs write(_nll.label_send_telegram); ?>'
-      );
-      $('#nodelist-' + e.node).css('cursor', 'pointer');
-      $('#nodelist-' + e.node).click(function () { sendTelegram(e.user); });
+    function _sb_list_node(e) {
+        $('#sbbs-nodelist-table').append(
+            '<tr>' +
+                '<th scope="row">' +
+                    (anl ? e.connection : (typeof e.node == 'number' ? e.node + 1 : e.connection)) +
+                '</th>' +
+                '<td id="nodelist-' + e.node + '">' +
+                    (e.user == '' ? e.status : ('<strong>' + e.user + '</strong> ' + e.action)) +
+                '</td>' +
+            '</tr>'
+        );
+        if (e.user != '' && <?xjs write(user.alias != settings.guest); ?>) {
+            $('#nodelist-' + e.node).attr('title', '<?xjs write(locale.strings.sidebar_node_list.label_send_telegram); ?>');
+            $('#nodelist-' + e.node).css('cursor', 'pointer');
+            $('#nodelist-' + e.node).click(function () { sendTelegram(e.user); });
+        }
+        if (typeof e.node == 'number' && e.user != '') niu++;
     }
-    if (typeof e.node == 'number' && e.user != '') niu++;
-  }
 
-  function _sb_nodelist() {
-  	$.getJSON('./api/system.ssjs?call=node-list', function (data) {
-      if (anl && !data.length) {
-        $('#sbbs-nodelist').parent().addClass('hidden');
-      } else {
-        if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
-  			$('#sbbs-nodelist').html(
-  				"<h4><?xjs write(_nll.label_title); ?></h4>" +
-  				'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
-    				'<thead><tr>' +
-              '<th>' + nch + '</th>' +
-              '<th><?xjs write(_nll.label_status_column); ?></th>' +
-            '</tr></thead>' +
-  				  '<tbody></tbody>' +
-  				'</table>'
-  			);
-        niu = 0;
-        if (!anl) {
-          const nodes = Array(nll);
-          data.forEach(function (e) {
-            if (typeof e.node == 'number') {
-              nodes[e.node] = e;
-            } else {
-              nodes.push(e); // Web user
-            }
-          });
-          for (var n = 0; n < nodes.length; n++) {
-            if (!nodes[n]) {
-              _sb_list_node({ node: n, user: '', status :'Waiting for call' });
+    function _sb_nodelist() {
+        $.getJSON('./api/system.ssjs?call=node-list', function (data) {
+            if (anl && !data.length) {
+                $('#sbbs-nodelist').parent().addClass('hidden');
             } else {
-              _sb_list_node(nodes[n], n);
+                if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
+    			$('#sbbs-nodelist').html(
+    				"<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>" +
+    				'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
+                    '<thead><tr>' +
+                    '<th>' + nch + '</th>' +
+                    '<th><?xjs write(locale.strings.sidebar_node_list.label_status_column); ?></th>' +
+                    '</tr></thead>' +
+    				'<tbody></tbody>' +
+    				'</table>'
+    			);
+                niu = 0;
+                if (!anl) {
+                    const nodes = Array(nll);
+                    data.forEach(function (e) {
+                        if (typeof e.node == 'number') {
+                            nodes[e.node] = e;
+                        } else {
+                            nodes.push(e); // Web user
+                        }
+                    });
+                    for (var n = 0; n < nodes.length; n++) {
+                        if (!nodes[n]) {
+                            _sb_list_node({ node: n, user: '', status :'Waiting for call' });
+                        } else {
+                            _sb_list_node(nodes[n], n);
+                        }
+                    }
+                } else {
+                    data.forEach(_sb_list_node);
+                }
+                // Spans with these classes are used in the 'system stats' sidebar module.
+                // Update the nodes in use / available counters there.
+                $('#sidebar').find('.sb-nodes-in-use').text(niu);
+                $('#sidebar').find('.sb-nodes-available').text(nll - niu);
             }
-          }
-        } else {
-          data.forEach(_sb_list_node);
-        }
-        // Spans with these classes are used in the 'system stats' sidebar module.
-        // Update the nodes in use / available counters there.
-        $('#sidebar').find('.sb-nodes-in-use').text(niu);
-        $('#sidebar').find('.sb-nodes-available').text(nll - niu);
-      }
-  	});
-  }
+  	    });
+    }
 
-  _sb_nodelist();
-  setInterval(_sb_nodelist, <?xjs write(settings.refresh_interval || 60000); ?>);
+    _sb_nodelist();
+    setInterval(_sb_nodelist, <?xjs write(settings.refresh_interval || 60000); ?>);
 
 </script>
-- 
GitLab


From b7d13ab4f45e609481f9391609c94aaf772936ac Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 02:48:29 -0500
Subject: [PATCH 367/752] local -> locale

---
 web/sidebar/.examples/003-systemStats.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/sidebar/.examples/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
index dd50b06b94..1a3afe8775 100644
--- a/web/sidebar/.examples/003-systemStats.xjs
+++ b/web/sidebar/.examples/003-systemStats.xjs
@@ -47,7 +47,7 @@
     <?xjs if (!settings.hide_empty_stats || system.stats.logons_today) { ?>
   		<tr>
   			<th scope="row"><?xjs write(locale.strings.sidebar_system_info.label_calls_today); ?></th>
-  			<td><?xjs write(local.group_numbers(system.stats.logons_today)); ?></td>
+  			<td><?xjs write(locale.group_numbers(system.stats.logons_today)); ?></td>
   		</tr>
     <?xjs } ?>
 
-- 
GitLab


From ebf8219efa2890362a21e6535f90f4e56a635126 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 03:51:01 -0500
Subject: [PATCH 368/752] Experimental and somewhat whisky-driven feature.
 root/api/events.ssjs is an EventSource, which loads callbacks from
 lib/events/*.js as needed; callbacks may emit events to be processed by the
 client; only those callbacks the client subscribes to are loaded and cycled
 each loop. lib/events/*.js must be return-style libraries and must implement
 a .cycle() method, which is called thru each loop in events.ssjs.
 root/index.xjs now creates the magic client-side const object _sbbs_events.
 Signature is {'event',callback(e)}.  Whenever 'event' is received by the
 client, 'callback(e)' is executed, where 'e' is the event object, and e.data
 is whatever data was sent (probably JSON). lib/events/nodelist.js is pretty
 cruddy and will be refined later, but serves as a server-side example.
 sidebar/.examples/001-nodelist.js registers a callback for the 'nodelist'
 event (pushed by lib/events/nodelist.js) and updates the sidebar nodelist
 based on data received.  Any Page or Sidebar module can do this. If this pans
 out, the idea is to replace various client-side async HTTP requests (unread
 message counts, telegrams, etc.) with events pushed from the server.

---
 web/lib/events/nodelist.js             | 40 +++++++++++++
 web/root/api/events.ssjs               | 48 +++++++++++++++
 web/root/index.xjs                     | 42 ++++++++-----
 web/sidebar/.examples/001-nodelist.xjs | 81 +++++++++++++-------------
 4 files changed, 156 insertions(+), 55 deletions(-)
 create mode 100644 web/lib/events/nodelist.js
 create mode 100644 web/root/api/events.ssjs

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
new file mode 100644
index 0000000000..bb538f452e
--- /dev/null
+++ b/web/lib/events/nodelist.js
@@ -0,0 +1,40 @@
+var last_run = 0;
+var frequency = 15;
+
+function cycle() {
+    if (time() - last_run <= frequency) return;
+    last_run = time();
+    log('running nodelist cycle');
+    var usr = new User(1);
+    reply = system.node_list.reduce(function (a, c, i) {
+        if (c.status !== 3) return a;
+        usr.number = c.useron;
+        a.push({
+            node : i,
+            status : format(NodeStatus[c.status], c.aux, c.extaux),
+            action : format(NodeAction[c.action], c.aux, c.extaux),
+            user : usr.alias,
+            connection : usr.connection
+        });
+        return a;
+    }, []);
+    for (var un = 1; un < system.lastuser; un++) {
+        usr.number = un;
+        if (usr.connection !== 'HTTP') continue;
+        if (usr.alias === settings.guest) continue;
+        if (usr.settings&USER_QUIET) continue;
+        if (usr.logontime < time() - settings.inactivity) continue;
+        var webAction = getSessionValue(usr.number, 'action');
+        if (webAction === null) continue;
+        reply.push({
+            status : '',
+            action : locale.strings.sidebar_node_list.label_status_column + ' ' + webAction,
+            user : usr.alias,
+            connection : 'W'
+        });
+    }
+    usr = undefined;
+    emit({ event: 'nodelist', data: reply });
+}
+
+this;
diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
new file mode 100644
index 0000000000..f92f3072d6
--- /dev/null
+++ b/web/root/api/events.ssjs
@@ -0,0 +1,48 @@
+load('modopts.js');
+const settings = get_mod_options('web');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
+
+http_reply.header['Cache-Control'] = 'no-cache';
+http_reply.header['Content-type'] = 'text/event-stream';
+http_reply.header['X-Accel-Buffering'] = 'no'; // probably not needed by everyone (nginx)
+
+const keepalive = 15;
+var last_send = 0;
+
+function ping() {
+    if (time() - last_send > keepalive) {
+        write(': ping\n\n');
+        last_send = time();
+    }
+}
+
+function emit(obj) {
+    log('emitting ' + JSON.stringify(obj));
+    Object.keys(obj).forEach(function (e) {
+        write(e + ': ' + (typeof obj[e] == 'object' ? JSON.stringify(obj[e]) : obj[e]) + '\n');
+    });
+    write('\n');
+    last_send = time();
+}
+
+const callbacks = [];
+if (file_isdir(settings.web_lib + 'events')) {
+    if (Array.isArray(http_request.query.subscribe)) {
+        http_request.query.subscribe.forEach(function (e) {
+            const base = file_getname(e).replace(file_getext(e), '');
+            const script = settings.web_lib + 'events/' + base + '.js';
+            if (file_exists(script)) callbacks.push(load({}, script));
+        });
+    }
+}
+
+ping();
+write('event: cocks\ndata: wangs\n\n');
+while (client.socket.is_connected) {
+    callbacks.forEach(function (e) {
+        e.cycle();
+    });
+    yield();
+    ping();
+}
diff --git a/web/root/index.xjs b/web/root/index.xjs
index adbf84f8d2..b2a655ef6a 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -98,6 +98,9 @@
 		<?xjs if (file_exists(settings.web_root + 'css/custom.css')) { ?>
 			<link href="./css/custom.css" rel="stylesheet">
 		<?xjs } ?>
+        <script type="text/javascript">
+            const _sbbs_events = {};
+        </script>
 	</head>
 
 	<body>
@@ -222,20 +225,31 @@
 
 		<script src="./js/offcanvas.js"></script>
 
-	<script>
-		$(document).ready(
-			function () {
-				$('a.dropdown-toggle').on(
-					"click", function (e) {
-						$(this).next('ul').toggle();
-						$(this).next('div').toggle();
-						e.stopPropagation();
-						e.preventDefault();
-					}
-				);
-			}
-		);
-	</script>
+    	<script>
+    		$(document).ready(
+    			function () {
+    				$('a.dropdown-toggle').on(
+    					"click", function (e) {
+    						$(this).next('ul').toggle();
+    						$(this).next('div').toggle();
+    						e.stopPropagation();
+    						e.preventDefault();
+    					}
+    				);
+    			}
+    		);
+            const _evtqs = Object.keys(_sbbs_events).reduce(function (a, c, i) {
+                return a + (i == 0 ? '?' : '&') + 'subscribe=' + c; }, ''
+            );
+            const _es = new EventSource('/api/events.ssjs' + _evtqs);
+            _es.onopen = function () {
+                console.log('Connected to event source');
+            }
+            Object.keys(_sbbs_events).forEach(function (e) {
+                console.log('adding event listener ' + e);
+                _es.addEventListener(e, _sbbs_events[e]);
+            });
+    	</script>
 
 	</body>
 
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 48657734ef..c125941750 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -26,51 +26,50 @@
         if (typeof e.node == 'number' && e.user != '') niu++;
     }
 
-    function _sb_nodelist() {
-        $.getJSON('./api/system.ssjs?call=node-list', function (data) {
-            if (anl && !data.length) {
-                $('#sbbs-nodelist').parent().addClass('hidden');
-            } else {
-                if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
-    			$('#sbbs-nodelist').html(
-    				"<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>" +
-    				'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
-                    '<thead><tr>' +
-                    '<th>' + nch + '</th>' +
-                    '<th><?xjs write(locale.strings.sidebar_node_list.label_status_column); ?></th>' +
-                    '</tr></thead>' +
-    				'<tbody></tbody>' +
-    				'</table>'
-    			);
-                niu = 0;
-                if (!anl) {
-                    const nodes = Array(nll);
-                    data.forEach(function (e) {
-                        if (typeof e.node == 'number') {
-                            nodes[e.node] = e;
-                        } else {
-                            nodes.push(e); // Web user
-                        }
-                    });
-                    for (var n = 0; n < nodes.length; n++) {
-                        if (!nodes[n]) {
-                            _sb_list_node({ node: n, user: '', status :'Waiting for call' });
-                        } else {
-                            _sb_list_node(nodes[n], n);
-                        }
+    function _sb_nodelist(evt) {
+        const data = JSON.parse(evt.data);
+        console.log('updating nodelist', data);
+        if (anl && !data.length) {
+            $('#sbbs-nodelist').parent().addClass('hidden');
+        } else {
+            if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
+			$('#sbbs-nodelist').html(
+				"<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>" +
+				'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
+                '<thead><tr>' +
+                '<th>' + nch + '</th>' +
+                '<th><?xjs write(locale.strings.sidebar_node_list.label_status_column); ?></th>' +
+                '</tr></thead>' +
+				'<tbody></tbody>' +
+				'</table>'
+			);
+            niu = 0;
+            if (!anl) {
+                const nodes = Array(nll);
+                data.forEach(function (e) {
+                    if (typeof e.node == 'number') {
+                        nodes[e.node] = e;
+                    } else {
+                        nodes.push(e); // Web user
+                    }
+                });
+                for (var n = 0; n < nodes.length; n++) {
+                    if (!nodes[n]) {
+                        _sb_list_node({ node: n, user: '', status :'Waiting for call' });
+                    } else {
+                        _sb_list_node(nodes[n], n);
                     }
-                } else {
-                    data.forEach(_sb_list_node);
                 }
-                // Spans with these classes are used in the 'system stats' sidebar module.
-                // Update the nodes in use / available counters there.
-                $('#sidebar').find('.sb-nodes-in-use').text(niu);
-                $('#sidebar').find('.sb-nodes-available').text(nll - niu);
+            } else {
+                data.forEach(_sb_list_node);
             }
-  	    });
+            // Spans with these classes are used in the 'system stats' sidebar module.
+            // Update the nodes in use / available counters there.
+            $('#sidebar').find('.sb-nodes-in-use').text(niu);
+            $('#sidebar').find('.sb-nodes-available').text(nll - niu);
+        }
     }
 
-    _sb_nodelist();
-    setInterval(_sb_nodelist, <?xjs write(settings.refresh_interval || 60000); ?>);
+    _sbbs_events.nodelist = _sb_nodelist;
 
 </script>
-- 
GitLab


From c94d75d030a0fa4e2d1399765c4854914df5c02b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 04:10:42 -0500
Subject: [PATCH 369/752] Added label_status_web to sidebar_node_list

---
 web/lib/locale/en_us.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/locale/en_us.ini b/web/lib/locale/en_us.ini
index 7d20ad6c02..0b6f5b4ed0 100644
--- a/web/lib/locale/en_us.ini
+++ b/web/lib/locale/en_us.ini
@@ -89,6 +89,7 @@ label_connection_column = Via
 label_node_column = Node
 label_send_telegram = Send a telegram
 label_status_column = Status
+label_status_web = browsing
 
 [sidebar_system_info]
 label_title = System Info
-- 
GitLab


From ffe16621b89b9ff49854f24ba97b7edebc7791cf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 04:10:58 -0500
Subject: [PATCH 370/752] Use label_status_web for webAction things.

---
 web/lib/events/nodelist.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index bb538f452e..e3655961dd 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -28,7 +28,7 @@ function cycle() {
         if (webAction === null) continue;
         reply.push({
             status : '',
-            action : locale.strings.sidebar_node_list.label_status_column + ' ' + webAction,
+            action : locale.strings.sidebar_node_list.label_status_web + ' ' + webAction,
             user : usr.alias,
             connection : 'W'
         });
-- 
GitLab


From c96d9ae27010da28f115b91cba72ff426d2b886c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 17:32:46 -0500
Subject: [PATCH 371/752] Remove debug line.

---
 web/root/api/events.ssjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index f92f3072d6..213b531a01 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -38,7 +38,6 @@ if (file_isdir(settings.web_lib + 'events')) {
 }
 
 ping();
-write('event: cocks\ndata: wangs\n\n');
 while (client.socket.is_connected) {
     callbacks.forEach(function (e) {
         e.cycle();
-- 
GitLab


From 03dccab14f31ce27ed2e8ac3b7be038e2e73653d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 17:33:18 -0500
Subject: [PATCH 372/752] Remove debug line.

---
 web/root/api/events.ssjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 213b531a01..31ae2ffeff 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -18,7 +18,6 @@ function ping() {
 }
 
 function emit(obj) {
-    log('emitting ' + JSON.stringify(obj));
     Object.keys(obj).forEach(function (e) {
         write(e + ': ' + (typeof obj[e] == 'object' ? JSON.stringify(obj[e]) : obj[e]) + '\n');
     });
-- 
GitLab


From 889f2e7d25aaa73af438dc1655ead778da58177d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 20:01:00 -0500
Subject: [PATCH 373/752] Push "who's online" data only when status/action has
 changed.

---
 web/lib/events/nodelist.js             | 130 +++++++++++++++++++------
 web/sidebar/.examples/001-nodelist.xjs |   3 +-
 2 files changed, 102 insertions(+), 31 deletions(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index e3655961dd..db16235f29 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -1,40 +1,110 @@
 var last_run = 0;
 var frequency = 15;
 
-function cycle() {
-    if (time() - last_run <= frequency) return;
-    last_run = time();
-    log('running nodelist cycle');
+const node_state = system.node_list.map(function (e, i) {
+    return {
+        status: -1,
+        action: -1,
+        aux: -1,
+        extaux: -1,
+        useron: -1
+    };
+});
+
+const web_state = {};
+
+function scan_nodes() {
+    var change = false;
+    system.node_list.forEach(function (e, i) {
+        const n = system.node_list[i];
+        if (n.status != node_state[i].status
+            || n.action != node_state[i].action
+            || n.useron != node_state[i].useron
+        ) {
+            change = true;
+            const obj = {
+                status: n.status,
+                action: n.action,
+                aux: n.aux,
+                extaux: n.extaux,
+                useron: n.useron
+            };
+            node_state[i] = obj;
+        }
+    });
+    return change;
+}
+
+function scan_web() {
+    var change = false;
+    const sessions = directory(system.data_dir + 'user/*.web');
+    sessions.forEach(function (e) {
+        const base = file_getname(e).replace(file_getext(e), '');
+        const un = parseInt(base);
+        if (isNaN(un) || un < 1 || un > system.lastuser) return;
+        if (time() - file_date(e) >= settings.inactivity) {
+            if (web_state[base]) {
+                delete web_state[base];
+                change = true;
+            }
+        } else {
+            const action = getSessionValue(un, 'action');
+            if (web_state[base] != action) {
+                web_state[base] = action;
+                change = true;
+            }
+        }
+    });
+    return change;
+}
+
+function scan() {
+    var out = [];
     var usr = new User(1);
-    reply = system.node_list.reduce(function (a, c, i) {
-        if (c.status !== 3) return a;
-        usr.number = c.useron;
-        a.push({
-            node : i,
-            status : format(NodeStatus[c.status], c.aux, c.extaux),
-            action : format(NodeAction[c.action], c.aux, c.extaux),
-            user : usr.alias,
-            connection : usr.connection
-        });
-        return a;
-    }, []);
-    for (var un = 1; un < system.lastuser; un++) {
-        usr.number = un;
-        if (usr.connection !== 'HTTP') continue;
-        if (usr.alias === settings.guest) continue;
-        if (usr.settings&USER_QUIET) continue;
-        if (usr.logontime < time() - settings.inactivity) continue;
-        var webAction = getSessionValue(usr.number, 'action');
-        if (webAction === null) continue;
-        reply.push({
-            status : '',
-            action : locale.strings.sidebar_node_list.label_status_web + ' ' + webAction,
-            user : usr.alias,
-            connection : 'W'
+    const node_change = scan_nodes();
+    const web_change = scan_web();
+    if (node_change) {
+        out = node_state.map(function (e, i) {
+            if (e.status != 3) {
+                return {
+                    node: i + 1,
+                    status: null,
+                    action: null,
+                    user: null,
+                    connection: ''
+                };
+            } else {
+                usr.number = e.useron;
+                return {
+                    node: i + 1,
+                    status: format(NodeStatus[e.status], e.aux, e.extaux),
+                    action: format(NodeAction[e.action], e.aux, e.extaux),
+                    user: usr.alias,
+                    connection: usr.connection == 'HTTP' ? 'BBS' : usr.connection
+                };
+            }
         });
     }
+    if (node_change || web_change) {
+        out = out.concat(Object.keys(web_state).map(function (e) {
+            usr.number = parseInt(e);
+            return {
+                node: 'W',
+                action: locale.strings.sidebar_node_list.label_status_web + ' ' + web_state[e],
+                user: usr.alias,
+                connection: 'HTTP'
+            };
+        }));
+    }
     usr = undefined;
-    emit({ event: 'nodelist', data: reply });
+    return out;
+}
+
+function cycle() {
+    if (time() - last_run <= frequency) return;
+    last_run = time();
+    const data = scan();
+    if (data.length) emit({ event: 'nodelist', data: JSON.stringify(data) });
 }
 
 this;
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index c125941750..12a4785560 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -8,6 +8,8 @@
     var niu = 0;
 
     function _sb_list_node(e) {
+        if (e.action === null || e.user === null) return;
+        console.log('NODE', e);
         $('#sbbs-nodelist-table').append(
             '<tr>' +
                 '<th scope="row">' +
@@ -28,7 +30,6 @@
 
     function _sb_nodelist(evt) {
         const data = JSON.parse(evt.data);
-        console.log('updating nodelist', data);
         if (anl && !data.length) {
             $('#sbbs-nodelist').parent().addClass('hidden');
         } else {
-- 
GitLab


From c76628b3811d0eb2596cd42881bf71abd6f6f0a5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 5 Jan 2019 20:05:34 -0500
Subject: [PATCH 374/752] Remove debug lines.

---
 web/root/index.xjs                     | 4 ----
 web/sidebar/.examples/001-nodelist.xjs | 1 -
 2 files changed, 5 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index b2a655ef6a..9c6c78d99f 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -242,11 +242,7 @@
                 return a + (i == 0 ? '?' : '&') + 'subscribe=' + c; }, ''
             );
             const _es = new EventSource('/api/events.ssjs' + _evtqs);
-            _es.onopen = function () {
-                console.log('Connected to event source');
-            }
             Object.keys(_sbbs_events).forEach(function (e) {
-                console.log('adding event listener ' + e);
                 _es.addEventListener(e, _sbbs_events[e]);
             });
     	</script>
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 12a4785560..b705c9bff0 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -9,7 +9,6 @@
 
     function _sb_list_node(e) {
         if (e.action === null || e.user === null) return;
-        console.log('NODE', e);
         $('#sbbs-nodelist-table').append(
             '<tr>' +
                 '<th scope="row">' +
-- 
GitLab


From e1d9deeeaf503b828cc69966b6dcd4569933cf1c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 00:21:03 -0500
Subject: [PATCH 375/752] Moved event subscription/handling stuff from
 index.xjs to common.js. Tidied up common.js, removed telegram polling,
 switched to telegram event subscription and handler.

---
 web/lib/events/telegram.js |  12 +++
 web/root/index.xjs         |  10 ---
 web/root/js/common.js      | 152 +++++++++++++++----------------------
 3 files changed, 75 insertions(+), 99 deletions(-)
 create mode 100644 web/lib/events/telegram.js

diff --git a/web/lib/events/telegram.js b/web/lib/events/telegram.js
new file mode 100644
index 0000000000..45ab4b5d9e
--- /dev/null
+++ b/web/lib/events/telegram.js
@@ -0,0 +1,12 @@
+var last_run = 0;
+var frequency = 15;
+
+function cycle() {
+    if (user.alias === settings.guest) return;
+    if (time() - last_run <= frequency) return;
+    last_run = time();
+    const tg = system.get_telegram(user.number);
+    if (tg !== null) emit({ event: 'telegram', data: JSON.stringify(tg) });
+}
+
+this;
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 9c6c78d99f..cae608b34e 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -98,9 +98,6 @@
 		<?xjs if (file_exists(settings.web_root + 'css/custom.css')) { ?>
 			<link href="./css/custom.css" rel="stylesheet">
 		<?xjs } ?>
-        <script type="text/javascript">
-            const _sbbs_events = {};
-        </script>
 	</head>
 
 	<body>
@@ -238,13 +235,6 @@
     				);
     			}
     		);
-            const _evtqs = Object.keys(_sbbs_events).reduce(function (a, c, i) {
-                return a + (i == 0 ? '?' : '&') + 'subscribe=' + c; }, ''
-            );
-            const _es = new EventSource('/api/events.ssjs' + _evtqs);
-            Object.keys(_sbbs_events).forEach(function (e) {
-                _es.addEventListener(e, _sbbs_events[e]);
-            });
     	</script>
 
 	</body>
diff --git a/web/root/js/common.js b/web/root/js/common.js
index 0649837d1a..7850c82ab9 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -1,45 +1,36 @@
 // How often to check for unread mail, new telegrams (milliseconds)
 var updateInterval = 60000;
+const _sbbs_events = {};
 
 function login(evt) {
-	if ($('#input-username').val() === '' ||
-		$('#input-password').val() === ''
-	) {
+	if ($('#input-username').val() == '' || $('#input-password').val() == '') {
 		return;
 	}
 	if (typeof evt !== 'undefined') evt.preventDefault();
-	$.ajax(
-		{	'url' : './api/auth.ssjs',
-			'method' : 'POST',
-			'data' : {
-				username : $('#input-username').val(),
-				password : $('#input-password').val()
-			}
+	$.ajax({
+        url: './api/auth.ssjs',
+		method: 'POST',
+		data: {
+			username : $('#input-username').val(),
+			password : $('#input-password').val()
 		}
-	).done(
-		function (data) {
-			if (data.authenticated) {
-				window.location.reload(true);
-			} else {
-				$('#login-form').append(
-					'<p class="text-danger">Login failed</p>'
-				);
-			}
+	}).done(function (data) {
+		if (data.authenticated) {
+			window.location.reload(true);
+		} else {
+			$('#login-form').append('<p class="text-danger">Login failed</p>');
 		}
-	);
+	});
 }
 
 function logout() {
-	$.ajax(
-		{	'url' : './api/auth.ssjs',
-			'method' : 'GET',
-			'data' : {
-				'logout' : true
-			}
-		}
-	).done(
-		function (data) { if (!data.authenticated) window.location.href = '/'; }
-	);
+	$.ajax({
+        url: './api/auth.ssjs',
+		method: 'GET',
+		data: { logout: true }
+	}).done(function (data) {
+        if (!data.authenticated) window.location.href = '/';
+    });
 }
 
 function scrollUp() {
@@ -49,15 +40,10 @@ function scrollUp() {
 }
 
 function getMailUnreadCount() {
-	$.getJSON(
-		'./api/forum.ssjs?call=get-mail-unread-count',
-		function (data) {
-			$('#badge-unread-mail').text(data.count < 1 ? '' : data.count);
-			$('#badge-unread-mail-inner').text(
-				data.count < 1 ? '' : data.count
-			);
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=get-mail-unread-count', function (data) {
+		$('#badge-unread-mail').text(data.count < 1 ? '' : data.count);
+		$('#badge-unread-mail-inner').text(data.count < 1 ? '' : data.count);
+	});
 }
 
 function sendTelegram(alias) {
@@ -67,72 +53,60 @@ function sendTelegram(alias) {
 		'placeholder="My message" name="telegram" id="telegram">'
 	);
 	$('#popUpModalActionButton').show();
-	$('#popUpModalActionButton').click(
-		function () {
-			$.getJSON(
-				'./api/system.ssjs?call=send-telegram&user=' +
-				alias + '&telegram=' + $('#telegram').val(),
-				function(data) {}
-			);
-			$('#popUpModal').modal('hide');
-		}
-	);
+	$('#popUpModalActionButton').click(function () {
+		$.getJSON(
+			'./api/system.ssjs?call=send-telegram&user=' +
+			alias + '&telegram=' + $('#telegram').val(),
+			function(data) {}
+		);
+        $('#popUpModal').modal('hide');
+	});
 	$('#popUpModal').modal('show');
 }
 
-function getTelegram() {
-	$.getJSON(
-		'./api/system.ssjs?call=get-telegram',
-		function (data) {
-			if (typeof data.telegram === 'undefined' ||
-				data.telegram === null
-			) {
-				return;
-			}
-			var tg = data.telegram.replace(
-				/\1./g, ''
-			).replace(
-				/\r?\n/g, '<br>'
-			);
-			$('#popUpModalTitle').html('New telegram(s) received');
-			$('#popUpModalBody').append(tg);
-			$('#popUpModalActionButton').hide();
-			$('#popUpModal').modal('show');
-		}
-	);
-}
-
 window.onload =	function () {
 
 	$('#button-logout').click(logout);
 	$('#button-login').click(login);
 	$('#form-login').submit(login);
 
-	$('#popUpModal').on(
-		'hidden.bs.modal',
-		function (e) {
-			$('#popUpModalActionButton').off('click');
-			$('#popUpModalTitle').empty();
-			$('#popUpModalBody').empty();
-		}
-	);
-	$("#popUpModalCloseButton").click(
-		function () {
-			$('#popUpModal').modal('hide');
-		}
-	);
+	$('#popUpModal').on('hidden.bs.modal', function (e) {
+		$('#popUpModalActionButton').off('click');
+		$('#popUpModalTitle').empty();
+		$('#popUpModalBody').empty();
+	});
+	$("#popUpModalCloseButton").click(function () {
+		$('#popUpModal').modal('hide');
+	});
 
 	setTimeout(scrollUp, 25);
 	window.onhashchange = scrollUp;
 
 	if ($('#button-logout').length > 0) {
 
-        getMailUnreadCount();
-        setInterval(getMailUnreadCount, updateInterval);
+        // Write backing event module
+        // Switch to event subscription & callback
+		getMailUnreadCount();
+		setInterval(getMailUnreadCount, updateInterval);
 
-        getTelegram();
-        setInterval(getTelegram, updateInterval);
+        _sbbs_events.telegram = function (e) {
+            const tg = JSON.parse(e.data).replace(/\1./g, '').replace(
+                /\r?\n/g, '<br>'
+            );
+            $('#popUpModalTitle').html('New telegram(s) received');
+            $('#popUpModalBody').append(tg);
+            $('#popUpModalActionButton').hide();
+            $('#popUpModal').modal('show');
+        }
+
+	}
 
-    }
+    const _evtqs = Object.keys(_sbbs_events).reduce(function (a, c, i) {
+        return a + (i == 0 ? '?' : '&') + 'subscribe=' + c; }, ''
+    );
+    const _es = new EventSource('/api/events.ssjs' + _evtqs);
+    Object.keys(_sbbs_events).forEach(function (e) {
+        _es.addEventListener(e, _sbbs_events[e]);
+    });
 
 }
-- 
GitLab


From 2e7359430deb91628d4170b7d63ff05dbbb5da4e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 00:48:22 -0500
Subject: [PATCH 376/752] Event emitter for mail (just unread mail count for
 now)

---
 web/lib/events/mail.js | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 web/lib/events/mail.js

diff --git a/web/lib/events/mail.js b/web/lib/events/mail.js
new file mode 100644
index 0000000000..e551279391
--- /dev/null
+++ b/web/lib/events/mail.js
@@ -0,0 +1,14 @@
+load(settings.web_lib + 'forum.js');
+
+var last_run = 0;
+var frequency = 10;
+
+function cycle() {
+    if (user.number < 1 || user.alias == settings.guest) return;
+    if (time() - last_run <= frequency) return;
+    last_run = time();
+    const count = user.stats.mail_waiting;
+    if (count > 0) emit({ event: 'mail', data: JSON.stringify({ count: count })});
+}
+
+this;
-- 
GitLab


From 3b86eacb58df8b34bef45ee35553e1162c03bcef Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 00:48:35 -0500
Subject: [PATCH 377/752] Removed unread mail poll. Added mail event
 subscription & handler.

---
 web/root/js/common.js | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index 7850c82ab9..b50474486b 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -39,13 +39,6 @@ function scrollUp() {
 	window.scrollBy(0, -document.getElementById('navbar').offsetHeight);
 }
 
-function getMailUnreadCount() {
-	$.getJSON('./api/forum.ssjs?call=get-mail-unread-count', function (data) {
-		$('#badge-unread-mail').text(data.count < 1 ? '' : data.count);
-		$('#badge-unread-mail-inner').text(data.count < 1 ? '' : data.count);
-	});
-}
-
 function sendTelegram(alias) {
 	$('#popUpModalTitle').html('Send a telegram to ' + alias);
 	$('#popUpModalBody').html(
@@ -84,10 +77,12 @@ window.onload =	function () {
 
 	if ($('#button-logout').length > 0) {
 
-        // Write backing event module
-        // Switch to event subscription & callback
-		getMailUnreadCount();
-		setInterval(getMailUnreadCount, updateInterval);
+        _sbbs_events.mail = function (e) {
+            const data = JSON.parse(e.data);
+            if (typeof data.count != 'number') return;
+            $('#badge-unread-mail').text(data.count < 1 ? '' : data.count);
+            $('#badge-unread-mail-inner').text(data.count < 1 ? '' : data.count);
+        }
 
         _sbbs_events.telegram = function (e) {
             const tg = JSON.parse(e.data).replace(/\1./g, '').replace(
-- 
GitLab


From b395d775dcf0e8a20e726552677250cbaf2bd8eb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 00:50:31 -0500
Subject: [PATCH 378/752] Less agressive rescan timer.

---
 web/lib/events/mail.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/mail.js b/web/lib/events/mail.js
index e551279391..9670c370b2 100644
--- a/web/lib/events/mail.js
+++ b/web/lib/events/mail.js
@@ -1,7 +1,7 @@
 load(settings.web_lib + 'forum.js');
 
 var last_run = 0;
-var frequency = 10;
+var frequency = 30;
 
 function cycle() {
     if (user.number < 1 || user.alias == settings.guest) return;
-- 
GitLab


From 73281a6cc688c575101ac72b08dd17691d85451a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 00:53:46 -0500
Subject: [PATCH 379/752] Emit an event if mail count is 0 but last mail count
 was > 0, to clear the indicator ir mail was picked up elsewhere.

---
 web/lib/events/mail.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/web/lib/events/mail.js b/web/lib/events/mail.js
index 9670c370b2..62d5478c23 100644
--- a/web/lib/events/mail.js
+++ b/web/lib/events/mail.js
@@ -2,13 +2,17 @@ load(settings.web_lib + 'forum.js');
 
 var last_run = 0;
 var frequency = 30;
+var last_count = 0;
 
 function cycle() {
     if (user.number < 1 || user.alias == settings.guest) return;
     if (time() - last_run <= frequency) return;
     last_run = time();
     const count = user.stats.mail_waiting;
-    if (count > 0) emit({ event: 'mail', data: JSON.stringify({ count: count })});
+    if (count > 0 || (count == 0 && last_count > 0)) {
+        emit({ event: 'mail', data: JSON.stringify({ count: count })});
+    }
+    last_count = count;
 }
 
 this;
-- 
GitLab


From a8817d975fcc000d8cb272963b23a3ae17953ed3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 01:01:15 -0500
Subject: [PATCH 380/752] Removed getMailUnreadCount() function. API (if
 anything is using it) will return user.stats.mail_waiting for a real user.

---
 web/lib/forum.js        | 16 ----------------
 web/root/api/forum.ssjs |  2 +-
 2 files changed, 1 insertion(+), 17 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 404b4a1e75..e59e8bffe0 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -242,22 +242,6 @@ function getUserPollData(sub, id) {
     return ret;
 }
 
-function getMailUnreadCount() {
-    var count = 0;
-    var msgBase = new MsgBase('mail');
-    msgBase.open();
-    for (var m = msgBase.first_msg; m <= msgBase.last_msg; m++) {
-        var index = msgBase.get_msg_header(m);
-        if (index === null) continue;
-        if (index.to_ext != user.number) continue;
-        if (index.attr&MSG_READ) continue;
-        if (index.attr&MSG_DELETE) continue;
-        count++;
-    }
-    msgBase.close();
-    return count;
-}
-
 function getMailHeaders(sent, ascending) {
     if (typeof sent !== 'undefined' &&
         sent &&
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 5d31b9c20c..ebd39d0f7b 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -27,7 +27,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
         switch(http_request.query.call[0].toLowerCase()) {
 
             case 'get-mail-unread-count':
-                reply.count = getMailUnreadCount();
+                reply.count = user.stats.mail_waiting;
                 break;
 
             case 'get-mail-body':
-- 
GitLab


From 031e03b0cfd09297ea1becc31dccc18e9b21ca06 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 6 Jan 2019 01:30:03 -0500
Subject: [PATCH 381/752] Cache loaded language file sections instead of
 rereading the file every time the string getter is called.

---
 web/lib/locale/en_us.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/web/lib/locale/en_us.js b/web/lib/locale/en_us.js
index fb94f98d7b..087d14d72b 100644
--- a/web/lib/locale/en_us.js
+++ b/web/lib/locale/en_us.js
@@ -11,11 +11,13 @@ function EN_US(name) {
     f.close();
     f = undefined;
 
+    const _strings = {};
     const strings = {};
     sections.forEach(function (e) {
         Object.defineProperty(strings, e, {
             enumerable: true,
             get: function () {
+                if (_strings[e]) return _strings[e];
                 var f = new File(ini_path);
                 f.open('r');
                 const o = f.iniGetObject(e);
@@ -32,6 +34,7 @@ function EN_US(name) {
                     }
                 }
                 f = undefined;
+                _strings[e] = o;
                 return o;
             }
         });
-- 
GitLab


From 25cf5d796c99850816710802bda02c0823365ff8 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Jan 2019 22:53:33 -0500
Subject: [PATCH 382/752] Use new NodeConnectionProper line courtesy of
 DigitalMan. Requires latest nodedefs.js. Fixes wonky connection-type display
 (HTTP when not actually).

---
 web/lib/events/nodelist.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index db16235f29..dc0ab7db75 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -7,7 +7,8 @@ const node_state = system.node_list.map(function (e, i) {
         action: -1,
         aux: -1,
         extaux: -1,
-        useron: -1
+        useron: -1,
+        connection : -1
     };
 });
 
@@ -27,7 +28,8 @@ function scan_nodes() {
                 action: n.action,
                 aux: n.aux,
                 extaux: n.extaux,
-                useron: n.useron
+                useron: n.useron,
+                connection: n.connection
             };
             node_state[i] = obj;
         }
@@ -80,7 +82,7 @@ function scan() {
                     status: format(NodeStatus[e.status], e.aux, e.extaux),
                     action: format(NodeAction[e.action], e.aux, e.extaux),
                     user: usr.alias,
-                    connection: usr.connection == 'HTTP' ? 'BBS' : usr.connection
+                    connection : NodeConnectionProper[e.connection] ? NodeConnectionProper[e.connection] : (e.connection + ' bps')
                 };
             }
         });
-- 
GitLab


From 356ad1cda673d15b06527a5a1acb888f3064ec06 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Jan 2019 23:02:56 -0500
Subject: [PATCH 383/752] Fixed show/hide of nodelist re: usage and
 active_node_list setting. Fixed nodes-in-use counter for systemstats.

---
 web/sidebar/.examples/001-nodelist.xjs | 72 +++++++++++++-------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index b705c9bff0..d5218fe6ef 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -9,6 +9,7 @@
 
     function _sb_list_node(e) {
         if (e.action === null || e.user === null) return;
+        niu++;
         $('#sbbs-nodelist-table').append(
             '<tr>' +
                 '<th scope="row">' +
@@ -29,45 +30,44 @@
 
     function _sb_nodelist(evt) {
         const data = JSON.parse(evt.data);
-        if (anl && !data.length) {
-            $('#sbbs-nodelist').parent().addClass('hidden');
-        } else {
-            if (anl) $('#sbbs-nodelist').parent().removeClass('hidden');
-			$('#sbbs-nodelist').html(
-				"<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>" +
-				'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
-                '<thead><tr>' +
-                '<th>' + nch + '</th>' +
-                '<th><?xjs write(locale.strings.sidebar_node_list.label_status_column); ?></th>' +
-                '</tr></thead>' +
-				'<tbody></tbody>' +
-				'</table>'
-			);
-            niu = 0;
-            if (!anl) {
-                const nodes = Array(nll);
-                data.forEach(function (e) {
-                    if (typeof e.node == 'number') {
-                        nodes[e.node] = e;
-                    } else {
-                        nodes.push(e); // Web user
-                    }
-                });
-                for (var n = 0; n < nodes.length; n++) {
-                    if (!nodes[n]) {
-                        _sb_list_node({ node: n, user: '', status :'Waiting for call' });
-                    } else {
-                        _sb_list_node(nodes[n], n);
-                    }
+        $('#sbbs-nodelist').parent().addClass('hidden');
+		$('#sbbs-nodelist').html(
+			"<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>" +
+			'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
+            '<thead><tr>' +
+            '<th>' + nch + '</th>' +
+            '<th><?xjs write(locale.strings.sidebar_node_list.label_status_column); ?></th>' +
+            '</tr></thead>' +
+			'<tbody></tbody>' +
+			'</table>'
+		);
+        niu = 0;
+        if (!anl) {
+            const nodes = Array(nll);
+            data.forEach(function (e) {
+                if (typeof e.node == 'number') {
+                    nodes[e.node] = e;
+                } else {
+                    nodes.push(e); // Web user
+                }
+            });
+            for (var n = 0; n < nodes.length; n++) {
+                if (!nodes[n]) {
+                    _sb_list_node({ node: n, user: '', status :'Waiting for call' });
+                } else {
+                    _sb_list_node(nodes[n], n);
+                    ns++;
                 }
-            } else {
-                data.forEach(_sb_list_node);
             }
-            // Spans with these classes are used in the 'system stats' sidebar module.
-            // Update the nodes in use / available counters there.
-            $('#sidebar').find('.sb-nodes-in-use').text(niu);
-            $('#sidebar').find('.sb-nodes-available').text(nll - niu);
+            $('#sbbs-nodelist').parent().removeClass('hidden');
+        } else {
+            data.forEach(_sb_list_node);
+            if (niu) $('#sbbs-nodelist').parent().removeClass('hidden');
         }
+        // Spans with these classes are used in the 'system stats' sidebar module.
+        // Update the nodes in use / available counters there.
+        $('#sidebar').find('.sb-nodes-in-use').text(niu);
+        $('#sidebar').find('.sb-nodes-available').text(nll - niu);
     }
 
     _sbbs_events.nodelist = _sb_nodelist;
-- 
GitLab


From 2092a147ab3dabebd6e742a429fbb02ba5dee43c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 7 Jan 2019 23:06:11 -0500
Subject: [PATCH 384/752] Whoops.

---
 web/sidebar/.examples/001-nodelist.xjs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index d5218fe6ef..0a6403d0b3 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -9,7 +9,6 @@
 
     function _sb_list_node(e) {
         if (e.action === null || e.user === null) return;
-        niu++;
         $('#sbbs-nodelist-table').append(
             '<tr>' +
                 '<th scope="row">' +
@@ -56,7 +55,6 @@
                     _sb_list_node({ node: n, user: '', status :'Waiting for call' });
                 } else {
                     _sb_list_node(nodes[n], n);
-                    ns++;
                 }
             }
             $('#sbbs-nodelist').parent().removeClass('hidden');
-- 
GitLab


From f290daed2ffbc8b44fc9b88f1df159365383a627 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 8 Jan 2019 02:14:08 -0500
Subject: [PATCH 385/752] Integrated Synchronet Inter-BBS messaging module into
 sidebar nodelist. You must add "nodelist_ibbs = true" to [web] in
 ctrl/modopts.ini to enable this. If a user is online on another system, an
 'Other systems' list will appear underneath your local list. Click on a user
 in the list to send them a telegram. (You can also click on local users to
 send them a telegram; this has been true and a hidden feature for some time.)
 Should probably do something with the mouse cursor to make this more obvious.

---
 web/lib/events/sbbsimsg.js             | 29 ++++++++++++
 web/root/api/sbbsimsg.ssjs             | 28 ++++++++++++
 web/sidebar/.examples/001-nodelist.xjs | 61 +++++++++++++++++++++++++-
 3 files changed, 117 insertions(+), 1 deletion(-)
 create mode 100644 web/lib/events/sbbsimsg.js
 create mode 100644 web/root/api/sbbsimsg.ssjs

diff --git a/web/lib/events/sbbsimsg.js b/web/lib/events/sbbsimsg.js
new file mode 100644
index 0000000000..c28c40e258
--- /dev/null
+++ b/web/lib/events/sbbsimsg.js
@@ -0,0 +1,29 @@
+load("sbbsdefs.js");
+load("nodedefs.js");
+const sbbsimsg = load({}, "sbbsimsg_lib.js");
+
+var last_run = 0;
+const frequency = 30;
+const timeout = 2500;
+
+function list() {
+    const state = {};
+    sbbsimsg.read_sys_list();
+    var sent = sbbsimsg.request_active_users();
+    sbbsimsg.poll_systems(sent, 0.25, timeout, function () { });
+    for (var i in sbbsimsg.sys_list) {
+        var sys = sbbsimsg.sys_list[i];
+        if (sys.users.length) {
+            state[sys.name] = { host: sys.host, users: sys.users };
+        }
+    }
+    emit({ event: 'sbbsimsg', data: JSON.stringify(state) });
+}
+
+function cycle() {
+    if (time() - last_run <= frequency) return;
+    last_run = time();
+    list();
+}
+
+this;
diff --git a/web/root/api/sbbsimsg.ssjs b/web/root/api/sbbsimsg.ssjs
new file mode 100644
index 0000000000..976e45f896
--- /dev/null
+++ b/web/root/api/sbbsimsg.ssjs
@@ -0,0 +1,28 @@
+load("sbbsdefs.js");
+load("nodedefs.js");
+load('modopts.js');
+var settings = get_mod_options('web');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
+const sbbsimsg = load({}, "sbbsimsg_lib.js");
+
+var reply = {};
+
+if (user.number > 0 && user.alias != settings.guest
+    && typeof http_request.query.call != 'undefined'
+    && http_request.query.call[0] == 'send_telegram'
+    && typeof http_request.query.host != 'undefined'
+    && typeof http_request.query.username != 'undefined'
+    && typeof http_request.query.message != 'undefined'
+) {
+    sbbsimsg.send_msg(
+        http_request.query.username[0] + '@' + http_request.query.host[0],
+        http_request.query.message[0],
+        user.alias
+    );
+}
+
+reply = JSON.stringify(reply);
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
+write(reply);
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 0a6403d0b3..9a75c60933 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -1,4 +1,7 @@
 <div id="sbbs-nodelist"></div>
+<?xjs if (settings.nodelist_ibbs) { ?>
+    <div id="sbbsimsg-nodelist"></div>
+<?xjs } ?>
 
 <script type="text/javascript">
 
@@ -22,7 +25,9 @@
         if (e.user != '' && <?xjs write(user.alias != settings.guest); ?>) {
             $('#nodelist-' + e.node).attr('title', '<?xjs write(locale.strings.sidebar_node_list.label_send_telegram); ?>');
             $('#nodelist-' + e.node).css('cursor', 'pointer');
-            $('#nodelist-' + e.node).click(function () { sendTelegram(e.user); });
+            $('#nodelist-' + e.node).click(function () {
+                sendTelegram(e.user);
+            });
         }
         if (typeof e.node == 'number' && e.user != '') niu++;
     }
@@ -70,4 +75,58 @@
 
     _sbbs_events.nodelist = _sb_nodelist;
 
+    <?xjs if (settings.nodelist_ibbs) { ?>
+
+
+        function _send_ibbs_telegram(sys, host, user) {
+            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
+        	$('#popUpModalBody').html('<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">');
+        	$('#popUpModalActionButton').show();
+        	$('#popUpModalActionButton').click(function () {
+        		$.getJSON(
+        			'./api/sbbsimsg.ssjs?call=send_telegram'
+                    + '&username=' + user
+                    + '&host=' + host
+                    + '&message=' + $('#telegram').val(),
+        			function (data) {
+
+                    }
+        		);
+                $('#popUpModal').modal('hide');
+        	});
+        	$('#popUpModal').modal('show');
+        }
+
+        _sbbs_events.sbbsimsg = function (e) {
+            const data = JSON.parse(e.data);
+            var users = 0;
+            $('#sbbsimsg-nodelist').addClass('hidden');
+            $('#sbbsimsg-nodelist').html('<h4>Other systems</h4>');
+            Object.keys(data).forEach(function (e, i) {
+                if (!data[e].users.length) return;
+                const id = 'sbbsimsg-nodelist-' + i;
+                $('#sbbsimsg-nodelist').append(
+                    '<table id="' + id + '" class="table table-condensed table-responsive table-striped">'
+                    + '<tr><td><strong>' + e + '</strong></td></tr>'
+                    + '</table>'
+                );
+                data[e].users.forEach(function (ee, ii) {
+                    const nid = id + '-' + ii;
+                    $('#' + id).append(
+                        '<tr>'
+                            + '<td id="' + nid + '">'
+                                + '<strong>' + ee.name + '</strong> ' + ee.action
+                            + '</td>'
+                        +'</tr>'
+                    );
+                    $('#' + nid).click(function () {
+                        _send_ibbs_telegram(e, data[e].host, ee.name);
+                    });
+                });
+                users += data[e].users.length;
+            });
+            if (users) $('#sbbsimsg-nodelist').removeClass('hidden');
+        }
+    <?xjs } ?>
+
 </script>
-- 
GitLab


From 059ab10e64f861689ba4c763384c08d03d862a54 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 8 Jan 2019 02:20:12 -0500
Subject: [PATCH 386/752] Bumped frequency to 60 seconds to match terminal
 version.

---
 web/lib/events/sbbsimsg.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/sbbsimsg.js b/web/lib/events/sbbsimsg.js
index c28c40e258..34bb30aaea 100644
--- a/web/lib/events/sbbsimsg.js
+++ b/web/lib/events/sbbsimsg.js
@@ -3,7 +3,7 @@ load("nodedefs.js");
 const sbbsimsg = load({}, "sbbsimsg_lib.js");
 
 var last_run = 0;
-const frequency = 30;
+const frequency = 60;
 const timeout = 2500;
 
 function list() {
-- 
GitLab


From aec6ecd78b09837cbff071c937580132c9aa5323 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 8 Jan 2019 02:20:55 -0500
Subject: [PATCH 387/752] Formatting (Other Systems) to match other headings.

---
 web/sidebar/.examples/001-nodelist.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 9a75c60933..551756223d 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -101,7 +101,7 @@
             const data = JSON.parse(e.data);
             var users = 0;
             $('#sbbsimsg-nodelist').addClass('hidden');
-            $('#sbbsimsg-nodelist').html('<h4>Other systems</h4>');
+            $('#sbbsimsg-nodelist').html('<h4>Other Systems</h4>');
             Object.keys(data).forEach(function (e, i) {
                 if (!data[e].users.length) return;
                 const id = 'sbbsimsg-nodelist-' + i;
-- 
GitLab


From 54f97102a797cbab2b981d5f3764807aeb8868b9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 8 Jan 2019 02:30:45 -0500
Subject: [PATCH 388/752] No ibbs telegrams for guests.

---
 web/sidebar/.examples/001-nodelist.xjs | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 551756223d..01fce8baa3 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -119,9 +119,11 @@
                             + '</td>'
                         +'</tr>'
                     );
-                    $('#' + nid).click(function () {
-                        _send_ibbs_telegram(e, data[e].host, ee.name);
-                    });
+                    if (<?xjs write(user.alias != settings.guest); ?>) {
+                        $('#' + nid).click(function () {
+                            _send_ibbs_telegram(e, data[e].host, ee.name);
+                        });
+                    }
                 });
                 users += data[e].users.length;
             });
-- 
GitLab


From 4adba2f2aba586e02081ff2277cc7e39b0179a4e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 8 Jan 2019 09:26:03 -0500
Subject: [PATCH 389/752] Show tooltip & alter mouse cursor when hovering over
 IBBS alias (send telegram). Fixed bug where if only web users are online,
 nodelist wouldn't display. Recently introduced I think.

---
 web/sidebar/.examples/001-nodelist.xjs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 01fce8baa3..f1e77eba11 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -9,6 +9,7 @@
     const nch = '<?xjs write(settings.active_node_list ? locale.strings.sidebar_node_list.label_connection_column : locale.strings.sidebar_node_list.label_node_column) ?>';
     const nll = <?xjs write(system.node_list.length); ?>;
     var niu = 0;
+    var ns = 0;
 
     function _sb_list_node(e) {
         if (e.action === null || e.user === null) return;
@@ -30,6 +31,7 @@
             });
         }
         if (typeof e.node == 'number' && e.user != '') niu++;
+        ns++;
     }
 
     function _sb_nodelist(evt) {
@@ -46,6 +48,7 @@
 			'</table>'
 		);
         niu = 0;
+        ns = 0;
         if (!anl) {
             const nodes = Array(nll);
             data.forEach(function (e) {
@@ -65,7 +68,7 @@
             $('#sbbs-nodelist').parent().removeClass('hidden');
         } else {
             data.forEach(_sb_list_node);
-            if (niu) $('#sbbs-nodelist').parent().removeClass('hidden');
+            if (ns) $('#sbbs-nodelist').parent().removeClass('hidden');
         }
         // Spans with these classes are used in the 'system stats' sidebar module.
         // Update the nodes in use / available counters there.
@@ -123,6 +126,8 @@
                         $('#' + nid).click(function () {
                             _send_ibbs_telegram(e, data[e].host, ee.name);
                         });
+                        $('#' + nid).attr('title', '<?xjs write(locale.strings.sidebar_node_list.label_send_telegram); ?>');
+                        $('#' + nid).css('cursor', 'pointer');
                     }
                 });
                 users += data[e].users.length;
-- 
GitLab


From 6cf259ed683b77016b4c8df8cd9aa488bf616e47 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 8 Jan 2019 13:40:02 -0500
Subject: [PATCH 390/752] Catch errors when loading event publishing modules or
 calling them periodically.  Log errors.  Cancel any module that causes a
 problem.

---
 web/root/api/events.ssjs | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 31ae2ffeff..fc1be55449 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,3 +1,4 @@
+load('sbbsdefs.js');
 load('modopts.js');
 const settings = get_mod_options('web');
 load(settings.web_directory + '/lib/init.js');
@@ -25,21 +26,30 @@ function emit(obj) {
     last_send = time();
 }
 
-const callbacks = [];
+const callbacks = {};
 if (file_isdir(settings.web_lib + 'events')) {
     if (Array.isArray(http_request.query.subscribe)) {
         http_request.query.subscribe.forEach(function (e) {
             const base = file_getname(e).replace(file_getext(e), '');
             const script = settings.web_lib + 'events/' + base + '.js';
-            if (file_exists(script)) callbacks.push(load({}, script));
+            try {
+                if (file_exists(script)) callbacks[e] = load({}, script);
+            } catch (err) {
+                log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
+            }
         });
     }
 }
 
 ping();
 while (client.socket.is_connected) {
-    callbacks.forEach(function (e) {
-        e.cycle();
+    Object.keys(callbacks).forEach(function (e) {
+        try {
+            callbacks[e].cycle();
+        } catch (err) {
+            log(LOG_ERR, 'Callback ' + e + ' failed: ' + err);
+            delete callbacks[e];
+        }
     });
     yield();
     ping();
-- 
GitLab


From b61ee50b3d68a4227a5da9b1ca5a93a71a46d8b6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 9 Jan 2019 17:24:29 -0500
Subject: [PATCH 391/752] Slightly hacky enter-to-submit for local & IBBS
 telegrams.

---
 web/root/js/common.js                  | 24 ++++++++++++++----------
 web/sidebar/.examples/001-nodelist.xjs | 21 +++++++++++++--------
 2 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index b50474486b..31b570b0b2 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -40,20 +40,24 @@ function scrollUp() {
 }
 
 function sendTelegram(alias) {
+    function send_tg(evt) {
+        if (typeof evt !== 'undefined') evt.preventDefault();
+        $.getJSON('./api/system.ssjs?call=send-telegram&user=' + alias + '&telegram=' + $('#telegram').val(),
+            function(data) {
+            }
+        );
+        $('#popUpModal').modal('hide');
+    }
 	$('#popUpModalTitle').html('Send a telegram to ' + alias);
 	$('#popUpModalBody').html(
-		'<input type="text" class="form-control" ' +
-		'placeholder="My message" name="telegram" id="telegram">'
+        '<form id="send-telegram-form">'
+		+ '<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">'
+        + '<input type="submit" value="submit" class="hidden">'
+        + '</form>'
 	);
 	$('#popUpModalActionButton').show();
-	$('#popUpModalActionButton').click(function () {
-		$.getJSON(
-			'./api/system.ssjs?call=send-telegram&user=' +
-			alias + '&telegram=' + $('#telegram').val(),
-			function(data) {}
-		);
-        $('#popUpModal').modal('hide');
-	});
+    $('#send-telegram-form').submit(send_tg);
+	$('#popUpModalActionButton').click(send_tg);
 	$('#popUpModal').modal('show');
 }
 
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index f1e77eba11..55f1143441 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -79,24 +79,29 @@
     _sbbs_events.nodelist = _sb_nodelist;
 
     <?xjs if (settings.nodelist_ibbs) { ?>
-
-
         function _send_ibbs_telegram(sys, host, user) {
-            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
-        	$('#popUpModalBody').html('<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">');
-        	$('#popUpModalActionButton').show();
-        	$('#popUpModalActionButton').click(function () {
+            function send_ibbs_tg(evt) {
+                if (typeof evt !== 'undefined') evt.preventDefault();
         		$.getJSON(
         			'./api/sbbsimsg.ssjs?call=send_telegram'
                     + '&username=' + user
                     + '&host=' + host
                     + '&message=' + $('#telegram').val(),
         			function (data) {
-
                     }
         		);
                 $('#popUpModal').modal('hide');
-        	});
+        	}
+            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
+        	$('#popUpModalBody').html(
+                '<form id="ibbs-telegram-form">'
+                + '<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">'
+                + '<input type="submit" value="submit" class="hidden">'
+                + '</form>'
+            );
+        	$('#popUpModalActionButton').show();
+            $('#ibbs-telegram-form').submit(send_ibbs_tg);
+        	$('#popUpModalActionButton').click(send_ibbs_tg);
         	$('#popUpModal').modal('show');
         }
 
-- 
GitLab


From c4313989560d8e8dda41ddc270f3911f903a1e37 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 10 Jan 2019 00:05:52 -0500
Subject: [PATCH 392/752] More show/hide fun.

---
 web/sidebar/.examples/001-nodelist.xjs | 32 +++++++++++++-------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 55f1143441..16a4b50c20 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -1,3 +1,4 @@
+<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>
 <div id="sbbs-nodelist"></div>
 <?xjs if (settings.nodelist_ibbs) { ?>
     <div id="sbbsimsg-nodelist"></div>
@@ -37,8 +38,8 @@
     function _sb_nodelist(evt) {
         const data = JSON.parse(evt.data);
         $('#sbbs-nodelist').parent().addClass('hidden');
+        $('#sbbs-nodelist').addClass('hidden');
 		$('#sbbs-nodelist').html(
-			"<h4><?xjs write(locale.strings.sidebar_node_list.label_title); ?></h4>" +
 			'<table id="sbbs-nodelist-table" class="table table-condensed table-responsive table-striped">' +
             '<thead><tr>' +
             '<th>' + nch + '</th>' +
@@ -68,7 +69,10 @@
             $('#sbbs-nodelist').parent().removeClass('hidden');
         } else {
             data.forEach(_sb_list_node);
-            if (ns) $('#sbbs-nodelist').parent().removeClass('hidden');
+            if (ns) {
+                $('#sbbs-nodelist').parent().removeClass('hidden');
+                $('#sbbs-nodelist').removeClass('hidden');
+            }
         }
         // Spans with these classes are used in the 'system stats' sidebar module.
         // Update the nodes in use / available counters there.
@@ -80,8 +84,10 @@
 
     <?xjs if (settings.nodelist_ibbs) { ?>
         function _send_ibbs_telegram(sys, host, user) {
-            function send_ibbs_tg(evt) {
-                if (typeof evt !== 'undefined') evt.preventDefault();
+            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
+        	$('#popUpModalBody').html('<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">');
+        	$('#popUpModalActionButton').show();
+        	$('#popUpModalActionButton').click(function () {
         		$.getJSON(
         			'./api/sbbsimsg.ssjs?call=send_telegram'
                     + '&username=' + user
@@ -91,21 +97,12 @@
                     }
         		);
                 $('#popUpModal').modal('hide');
-        	}
-            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
-        	$('#popUpModalBody').html(
-                '<form id="ibbs-telegram-form">'
-                + '<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">'
-                + '<input type="submit" value="submit" class="hidden">'
-                + '</form>'
-            );
-        	$('#popUpModalActionButton').show();
-            $('#ibbs-telegram-form').submit(send_ibbs_tg);
-        	$('#popUpModalActionButton').click(send_ibbs_tg);
+        	});
         	$('#popUpModal').modal('show');
         }
 
         _sbbs_events.sbbsimsg = function (e) {
+            console.log('tg', e);
             const data = JSON.parse(e.data);
             var users = 0;
             $('#sbbsimsg-nodelist').addClass('hidden');
@@ -137,7 +134,10 @@
                 });
                 users += data[e].users.length;
             });
-            if (users) $('#sbbsimsg-nodelist').removeClass('hidden');
+            if (users) {
+                $('#sbbsimsg-nodelist').removeClass('hidden');
+                $('#sbbs-nodelist').parent().removeClass('hidden');
+            }
         }
     <?xjs } ?>
 
-- 
GitLab


From 432a611fdd4d1b83e45d1243d23d53521bb73ce9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 10 Jan 2019 00:18:47 -0500
Subject: [PATCH 393/752] No need to load settings here, I think.

---
 web/lib/forum.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index e59e8bffe0..1b50fd66c6 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,6 +1,5 @@
 load('sbbsdefs.js');
 load('modopts.js');
-var settings = get_mod_options('web');
 
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
-- 
GitLab


From ed92241b6c5f4c7c8064c7762de12597d716c4e7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 10 Jan 2019 00:24:24 -0500
Subject: [PATCH 394/752] Redeclaration of 'settings'

---
 web/lib/init.js          | 2 +-
 web/root/api/events.ssjs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index f2f74ce2a1..3ea18010f9 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,7 +1,7 @@
 load('sbbsdefs.js');
 load('modopts.js');
 
-var settings = get_mod_options('web');
+if (!settings) var settings = get_mod_options('web');
 
 // Paths
 settings.web_directory = fullpath(
diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index fc1be55449..0758020612 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,6 +1,6 @@
 load('sbbsdefs.js');
 load('modopts.js');
-const settings = get_mod_options('web');
+var settings = get_mod_options('web');
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 
-- 
GitLab


From abf0412c4d4f9d9c6a1aa8226bffba590fbb4768 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 10 Jan 2019 10:32:17 -0800
Subject: [PATCH 395/752] Fix usernum parsing bug in scan_web(): numbers
 beginning w/0 are interpretted as octal by parseInt() by default.

---
 web/lib/events/nodelist.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index dc0ab7db75..adf3e6e245 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -42,7 +42,7 @@ function scan_web() {
     const sessions = directory(system.data_dir + 'user/*.web');
     sessions.forEach(function (e) {
         const base = file_getname(e).replace(file_getext(e), '');
-        const un = parseInt(base);
+        const un = parseInt(base, 10);
         if (isNaN(un) || un < 1 || un > system.lastuser) return;
         if (time() - file_date(e) >= settings.inactivity) {
             if (web_state[base]) {
-- 
GitLab


From 8cb6a31bf8bb40156a05711f8b5e928af72a5e97 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 10 Jan 2019 19:13:13 -0500
Subject: [PATCH 396/752] Provide radix to parseInt so that we get the correct
 user number.

---
 web/lib/events/nodelist.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index adf3e6e245..f86978fded 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -89,7 +89,7 @@ function scan() {
     }
     if (node_change || web_change) {
         out = out.concat(Object.keys(web_state).map(function (e) {
-            usr.number = parseInt(e);
+            usr.number = parseInt(e, 10);
             return {
                 node: 'W',
                 action: locale.strings.sidebar_node_list.label_status_web + ' ' + web_state[e],
-- 
GitLab


From e042ca287b7d0df6e5875af20f71b970b1fe510d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 10 Jan 2019 22:15:27 -0500
Subject: [PATCH 397/752] Unbreak enter-to-send.

---
 web/root/js/common.js                  |  8 +++-----
 web/sidebar/.examples/001-nodelist.xjs | 24 ++++++++++++++++--------
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index 31b570b0b2..53151221af 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -42,10 +42,8 @@ function scrollUp() {
 function sendTelegram(alias) {
     function send_tg(evt) {
         if (typeof evt !== 'undefined') evt.preventDefault();
-        $.getJSON('./api/system.ssjs?call=send-telegram&user=' + alias + '&telegram=' + $('#telegram').val(),
-            function(data) {
-            }
-        );
+        $.getJSON('./api/system.ssjs?call=send-telegram&user=' + alias + '&telegram=' + $('#telegram').val(), function(data) {
+        });
         $('#popUpModal').modal('hide');
     }
 	$('#popUpModalTitle').html('Send a telegram to ' + alias);
@@ -55,9 +53,9 @@ function sendTelegram(alias) {
         + '<input type="submit" value="submit" class="hidden">'
         + '</form>'
 	);
-	$('#popUpModalActionButton').show();
     $('#send-telegram-form').submit(send_tg);
 	$('#popUpModalActionButton').click(send_tg);
+    $('#popUpModalActionButton').show();
 	$('#popUpModal').modal('show');
 }
 
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 16a4b50c20..1e40c77740 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -84,20 +84,28 @@
 
     <?xjs if (settings.nodelist_ibbs) { ?>
         function _send_ibbs_telegram(sys, host, user) {
-            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
-        	$('#popUpModalBody').html('<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">');
-        	$('#popUpModalActionButton').show();
-        	$('#popUpModalActionButton').click(function () {
-        		$.getJSON(
-        			'./api/sbbsimsg.ssjs?call=send_telegram'
+            function send_ibbs_tg(evt) {
+                if (typeof evt !== 'undefined') evt.preventDefault();
+                $.getJSON(
+                    './api/sbbsimsg.ssjs?call=send_telegram'
                     + '&username=' + user
                     + '&host=' + host
                     + '&message=' + $('#telegram').val(),
-        			function (data) {
+                    function (data) {
                     }
         		);
                 $('#popUpModal').modal('hide');
-        	});
+        	};
+            $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
+        	$('#popUpModalBody').html(
+                '<form id="ibbs-telegram-form">'
+                + '<input type="text" class="form-control" placeholder="My message" name="telegram" id="telegram">'
+                + '<input type="submit" value="submit" class="hidden">'
+                + '</form>'
+            );
+        	$('#popUpModalActionButton').show();
+            $('#ibbs-telegram-form').submit(send_ibbs_tg);
+            $('#popUpModalActionButton').click(send_ibbs_tg);
         	$('#popUpModal').modal('show');
         }
 
-- 
GitLab


From e8ccff7be48591703c74c3a888118fcae5d2d0d3 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 10 Jan 2019 22:19:33 -0500
Subject: [PATCH 398/752] Remove debug output.

---
 web/sidebar/.examples/001-nodelist.xjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 1e40c77740..6a9a352fcf 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -110,7 +110,6 @@
         }
 
         _sbbs_events.sbbsimsg = function (e) {
-            console.log('tg', e);
             const data = JSON.parse(e.data);
             var users = 0;
             $('#sbbsimsg-nodelist').addClass('hidden');
-- 
GitLab


From 935a8a16893709fc057ac56f5c17a84174df0dad Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 10 Jan 2019 23:19:41 -0800
Subject: [PATCH 399/752] Fix 2 errors: TypeError: ee.protocol[0] is undefined:
 sometimes there just ain't no protocol? graphic.js line 113: TypeError: bin
 is null: the BBS previews are LZ-compressed and thus must be passed through
 lib.decode_preview() to get the raw data.

---
 web/pages/.examples/More/999-sbbslist.xjs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web/pages/.examples/More/999-sbbslist.xjs b/web/pages/.examples/More/999-sbbslist.xjs
index 900e663400..0ef2847566 100644
--- a/web/pages/.examples/More/999-sbbslist.xjs
+++ b/web/pages/.examples/More/999-sbbslist.xjs
@@ -40,7 +40,7 @@
 
 	function bbs_preview(bbs) {
 	    var graphic=new Graphic();
-	    graphic.base64_decode(bbs.preview);
+	    graphic.BIN = lib.decode_preview(bbs.preview);
 		writeln(
 			'<div class="pull-left" ' +
 			'style="margin-right:5px;" ' +
@@ -71,6 +71,8 @@
 				Sysops : e.sysop.map(function (ee) { return ee.name; }).join(', '),
 				Services : e.service.map(
 					function (ee) {
+                        if (!ee.protocol)
+                            return '';
 						var r = ee.protocol[0].toUpperCase() + ee.protocol.slice(1) + ': ' + ee.address;
 						if (typeof ee.port === 'number') r += ':' + ee.port;
 						return r;
-- 
GitLab


From aac14b37010255abdfa4516e0d9f5b61025ff4f5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 11 Jan 2019 13:58:07 -0500
Subject: [PATCH 400/752] Added ANSI parsing/rendering, output to PNG or to a
 canvas element with speed emulation/scrolling. Demo:
 https://bbs.electronicchicken.com/?page=More/007-ansiview.xjs&path=/!echicken/ANGRYCKN.ANS

---
 web/root/js/graphics-converter.js | 246 ++++++++++++++++++++++++------
 1 file changed, 201 insertions(+), 45 deletions(-)

diff --git a/web/root/js/graphics-converter.js b/web/root/js/graphics-converter.js
index f339964922..7bd086a0c7 100644
--- a/web/root/js/graphics-converter.js
+++ b/web/root/js/graphics-converter.js
@@ -1,5 +1,11 @@
 function GraphicsConverter(spritesheet_src, font_width, font_height, spritesheet_columns, spritesheet_rows) {
 
+    if (!spritesheet_src) spritesheet_src = '/images/cp437-ibm-vga8.png';
+    if (!font_width) font_width = 8;
+    if (!font_height) font_height = 16;
+    if (!spritesheet_columns) spritesheet_columns = 64;
+    if (!spritesheet_rows) spritesheet_rows = 4;
+
     const COLORS = [
         "#000000", // Black
         "#0000A8", // Blue
@@ -19,37 +25,54 @@ function GraphicsConverter(spritesheet_src, font_width, font_height, spritesheet
         "#FFFFFF"  // White
     ];
 
+    const ANSI_COLORS = [
+        "#000000", // Black
+        "#A80000", // Red
+        "#00A800", // Green
+        "#A85400", // Brown
+        "#0000A8", // Blue
+        "#A800A8", // Magenta
+        "#00A8A8", // Cyan
+        "#A8A8A8", // Light Grey
+        "#545454", // Dark Grey (High Black)
+        "#FC5454", // Light Red
+        "#54FC54", // Light Green
+        "#FCFC54", // Yellow (High Brown)
+        "#5454FC", // Light Blue
+        "#FC54FC", // Light Magenta
+        "#54FCFC", // Light Cyan
+        "#FFFFFF"  // White
+    ];
+
     function get_workspace(cols, rows, callback) {
 
         const container = document.createElement('div');
 
         const canvas = document.createElement('canvas');
         const ctx = canvas.getContext('2d');
-        canvas.width = cols * font_width;
-        canvas.height = rows * font_height;
+        ctx.canvas.width = cols * font_width;
+        ctx.canvas.height = rows * font_height;
+        ctx.fillStyle = COLORS[0];
+        ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 
         const spritesheet_canvas = document.createElement('canvas');
         const spritesheet_ctx = spritesheet_canvas.getContext('2d');
-        spritesheet_canvas.width = spritesheet_columns * font_width;
-        spritesheet_canvas.height = spritesheet_rows * font_height;
+        spritesheet_ctx.canvas.width = spritesheet_columns * font_width;
+        spritesheet_ctx.canvas.height = spritesheet_rows * font_height;
+        spritesheet_canvas.style.display = 'none';
 
         container.appendChild(canvas);
         container.appendChild(spritesheet_canvas);
 
         const img = new Image();
-        img.addEventListener(
-            'load', function () {
-                spritesheet_ctx.drawImage(img, 0, 0);
-                callback(
-                    {   container : container,
-                        canvas : canvas,
-                        ctx : ctx,
-                        spritesheet_canvas : spritesheet_canvas,
-                        spritesheet_ctx : spritesheet_ctx
-                    }
-                )
-            }
-        );
+        img.addEventListener('load', function () {
+            spritesheet_ctx.drawImage(img, 0, 0);
+            callback({
+                container: container,
+                ctx: ctx,
+                spritesheet_ctx: spritesheet_ctx
+            })
+        });
         img.src = spritesheet_src;
 
     }
@@ -82,44 +105,177 @@ function GraphicsConverter(spritesheet_src, font_width, font_height, spritesheet
     }
 
     function get_png(workspace, callback) {
-        const data = workspace.canvas.toDataURL();
+        const data = workspace.ctx.canvas.toDataURL();
         const img = new Image();
-        img.addEventListener('load', function () { callback(img); });
+        img.addEventListener('load', function () {
+            callback(img);
+        });
         img.src = data;
     }
 
     this.from_bin = function (bin, cols, rows, callback) {
+        get_workspace(cols, rows, function (workspace) {
+            var x = 0;
+            var y = 0;
+            for (var n = 0; n < cols * rows * 2; n = n + 2) {
+                const char = bin.substr(n, 1).charCodeAt(0);
+                const attr = bin.substr(n + 1, 1).charCodeAt(0);
+                put_character(
+                    workspace,
+                    get_character(workspace, char),
+                    x * font_width,
+                    y * font_height,
+                    COLORS[attr&15],
+                    COLORS[(attr>>4)&7]
+                );
+                x++;
+                if (x >= cols) {
+                    x = 0;
+                    y++;
+                }
+            }
+            get_png(workspace, function (img) {
+                delete_workspace(workspace);
+                callback(img);
+            });
+        });
+    }
 
-        get_workspace(
-            cols, rows, function (workspace) {
-                var x = 0;
-                var y = 0;
-                for (var n = 0; n < cols * rows * 2; n = n + 2) {
-                    const char = bin.substr(n, 1).charCodeAt(0);
-                    const attr = bin.substr(n + 1, 1).charCodeAt(0);
+    this.from_ans = function (ans, target, rate) {
+        var x = 0;
+        var y = 0;
+        var _x = 0;
+        var _y = 0;
+        var fg = 7;
+        var bg = 0;
+        var high = 0;
+        var match;
+        var opts;
+        const re = /^\u001b\[((?:[0-9]{0,2};?)*)([a-zA-Z])/;
+        const data = [[]];
+        const seq = [];
+        while (ans.length) {
+            match = re.exec(ans);
+            if (match !== null) {
+                ans = ans.substr(match[0].length);
+                opts = match[1].split(';').map(function (e) {
+                    return parseInt(e);
+                });
+                switch (match[2]) {
+                    case 'A':
+						y = Math.max(y - (opts[0] || 1), 0);
+						break;
+					case 'B':
+						y += (opts[0] || 1);
+                        data[y] = [];
+						break;
+					case 'C':
+						x = Math.min(x + (opts[0] || 1), 79);
+						break;
+					case 'D':
+						x = Math.max(x - (opts[0] || 1), 0);
+						break;
+					case 'f':
+                    case 'H':
+						y = opts[0] || 1;
+						x = opts[1] || 1;
+                        if (y >= data.length) data[y] = [];
+						break;
+					case 'm':
+						for (var o in opts) {
+							var i = parseInt(opts[o]);
+							if (i == 0) {
+								fg = 7;
+								bg = 0;
+								high = 0;
+							} else if (i == 1) {
+								high = 1;
+							} else if (i == 5) {
+								// blink
+							} else if (i >= 30 && i <= 37) {
+								fg = i - 30;
+							} else if (i >= 40 && i <= 47) {
+								bg = i - 40;
+							}
+						}
+						break;
+					case 's': // push xy
+						_x = x;
+                        _y = y;
+						break;
+					case 'u': // pop xy
+						x = _x;
+                        y = _y;
+						break;
+					case 'J':
+						if (opts.length == 1 && opts[0] == 2) {
+                            for (var yy = 0; yy < data.length; yy++) {
+                                if (!Array.isArray(data[yy])) data[yy] = [];
+								for (var xx = 0; xx < 80; xx++) {
+                                    data[yy][xx] = { c: ' ', a: fg|(bg<<3)|(high<<7) };
+								}
+							}
+						}
+						break;
+					case 'K':
+                        for (var xx = 0; xx < 80; xx++) {
+                            data[y][xx] = { c: ' ', a: fg|(bg<<3)|(high<<7) };
+                        }
+						break;
+					default:
+						// Unknown or unimplemented command
+						break;
+                }
+            } else {
+                var ch = ans.substr(0, 1);
+                switch (ch) {
+                    case '\x1a':
+                        ans = '';
+                        break;
+                    case '\r':
+                        y++;
+                        data.push([]);
+                        break;
+                    case '\n':
+                        x = 0;
+                        break;
+                    default:
+                        data[y][x] = { c: ch, a: fg|(bg<<3)|(high<<7) };
+                        seq.push({y:y,x:x});
+                        x++;
+                        if (x > 79) {
+                            x = 0;
+                            y++;
+                            data.push([]);
+                        }
+                        break;
+                }
+                ans = ans.substr(1);
+            }
+        }
+        get_workspace(80, data.length, function (workspace) {
+            if (typeof target == 'string') {
+                $(target).prepend(workspace.container);
+            }
+            seq.forEach(function (e, i) {
+                function draw() {
                     put_character(
                         workspace,
-                        get_character(workspace, char),
-                        x * font_width,
-                        y * font_height,
-                        COLORS[attr&15],
-                        COLORS[(attr>>4)&7]
+                        get_character(workspace, data[e.y][e.x].c.charCodeAt(0)),
+                        e.x * font_width,
+                        e.y * font_height,
+                        ANSI_COLORS[(data[e.y][e.x].a&7) + ((data[e.y][e.x].a&(1<<7)) ? 8 : 0)],
+                        ANSI_COLORS[(data[e.y][e.x].a>>3)&7]
                     );
-                    x++;
-                    if (x >= cols) {
-                        x = 0;
-                        y++;
-                    }
                 }
-                get_png(
-                    workspace, function (img) {
-                        delete_workspace(workspace);
-                        callback(img);
-                    }
-                );
-            }
-        );
-
+                if (rate) {
+                    setTimeout(draw, i * Math.ceil(1000 / (rate / 8)));
+                } else {
+                    draw();
+                }
+            });
+            if (typeof target == 'function') get_png(workspace, target);
+        });
     }
 
 }
-- 
GitLab


From 44d975e8b8be6044db7870f9f1e9ace8ab9ab753 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Jan 2019 10:08:40 -0500
Subject: [PATCH 401/752] Convert to a return-style library. Added get_url
 method for s2s fetch of ftelnet script src.

---
 web/lib/ftelnet.js | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index 9673a5fe39..9d32c8e971 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -1,4 +1,6 @@
-function getSplash() {
+require('http.js', 'HTTPRequest');
+
+function get_splash() {
     var DefaultSplash = 'G1swbQ0KG1syOEMbWzE7MzBtICAgG1swbRtbMTlDG1szNG3cG1sxbdwbWzBtICAgG1szNG3cDQobWzM3bRtbMTNDG1sxbSAbWzMzbSAbWzBtG1sxM0MbWzM0bdwbWzM3bSAgG1szNG3c3LAbWzQ0OzMwbbIbWzQwOzM3bRtbMTFDG1szNG0gIBtbMW3c3xtbMG0bWzlDG1sxOzMwbWdqL9rkDQobWzBtG1s2QxtbMW1zeW5jaHJvbmV0G1swbSAgIBtbMTszMG3c3BtbMG0gG1sxOzMwbd8bWzBtICAgG1szNG3+G1szN20bWzVDG1szNG0gIN+y29zcG1szN20gICAbWzM0bdzcICAgG1sxbd4bWzA7MzRt3Q0KG1szN20bWzdDG1szNG3cG1szN20gG1szNG3+G1szN20gICAg3NzcG1sxbbGxsBtbMG0gICAgG1szNG3c3Nzc2xtbMTs0NG3cICD+G1swOzM0bdvc3tsbWzQ0OzMwbbAbWzQwOzM0bbLfIN4bWzE7NDRtsLAbWzA7MzRtsbDcG1sxbdwbWzQ2bbIbWzA7MzRtIBtbMzdt3xtbMTszMG3fG1swbdsbWzE7NDdt3Nzc3NwbWzBtsiAgIBtbMzRt3xtbMzdtICAbWzM0bd/f3NwNChtbMzdtICAgIBtbMTszNG3cG1swOzM0bd8bWzM3bSAgINwbWzFt3BtbNDdt3BtbNDBt29sbWzQ3bdvb27EbWzQwbbAbWzBtICAbWzM0bdwbWzE7NDRt3NwbWzQwbdvf3xtbMDszNG3fG1szN20g3NzcIBtbMzRt3BtbMTs0NG2wG1swOzM0bdvfG1szN20g3NzcIBtbMzRt3htbMTs0NG2yG1swOzM0bdwbWzQ0OzMybbAbWzQ2OzM0bbIbWzE7NDBt398bWzA7MzRt3xtbMzZtIBtbMzdtICAbWzE7NDc7MzBt3BtbMzdtstvb2xtbNDBtsRtbMG0gINzc3Nzc3CAbWzE7MzRtsN0NChtbMG0gIBtbMTszNG0g3htbMDszNG3dG1szN20gICAbWzFtsBtbNDdtstvb29/f29uyG1swbd0gG1sxOzM0bbEbWzQ2bbIbWzQ3bbIbWzQ2bbIbWzQwbbIbWzBtIBtbMTs0N23e29vbG1s0MG2xG1swbSAbWzE7MzRt3BtbNDRtshtbMDszNG3fG1szN23cG1sxOzQ3bbCx29uyG1swbd0gG1s0NjszNG3cG1sxOzQwbd8bWzBtIBtbMTszMG3cG1swbdwbWzE7NDdtsLCwsLAbWzBt3NsbWzE7NDdtstvbshtbNDBtsBtbMG0g2xtbMTs0N23b27AbWzBtIBtbMzRt3N8bWzFt39zcG1swOzM0bdwNChtbMzdtICAgIBtbNDY7MzBtshtbNDA7MzRtICAbWzE7MzBt3htbNDc7MzdtsbLb29wbWzBt3CDfG1sxOzQ3bd/fG1swbdvcIBtbMTszNG3f3xtbMDszNG0gG1sxbbEbWzBtIBtbMTs0N23e29sbWzQwbbIbWzBt3RtbMTszNG3eG1s0Nm2yG1swbSAbWzE7MzBt3BtbMG2y3xtbMTs0N22xstvbG1s0MG2xG1swbSAg3BtbMTs0N22wsbKysrKysrEbWzBtIBtbMTs0NzszMG2wG1szN23b29sbWzQwbbEbWzBtIN4bWzE7NDdt3tuyG1swbd0bWzM0bd8bWzE7NDRtsBtbNDBt2xtbNDdtsrIbWzQwbbEbWzA7MzRtsrEbWzM3bSAbWzM0bbANCiAgG1sxbSAbWzA7MzRt3NwbWzE7NDRtsBtbMDszNG3cG1szN20gG1sxOzMwbd8bWzBt3xtbMTs0N23f39vb3BtbMG3cICAbWzE7MzBt3BtbMG3c3NwbWzFtsBtbMzBt3RtbMzRtsBtbMG0gG1sxOzQ3bd7b2xtbNDBtsbAbWzBt3BtbMzZt3xtbMzdtICAbWzM0bdwbWzM3bSCwG1sxOzQ3bbLbsrAbWzBtIBtbMW2wG1s0N22xstvb39vb2xtbNDBtshtbMG3dIBtbMTszMG3eG1s0NzszN22x29uxG1swbdzc2xtbMTs0N23b2xtbNDBtstzcG1swbdwbWzFt3BtbMG3c3CDcIBtbMTszNG2wG1swOzQ0OzMwbbIbWzBtDQobWzM0bSAg2xtbMTs0NG2wshtbMDszNG0gG1sxOzQ0bbEbWzQwbbEbWzA7NDY7MzRtshtbNDBt3NwbWzM3bSDf3xtbMTs0N23f39wbWzBt3CAbWzE7NDdtsdvb2xtbMG3dIBtbMTszMG3eG1s0NzszN23e29sbWzQwbbCyG1s0N23b29wbWzQwbdwbWzBt3BtbMzRtIBtbMTszN20gG1s0N22ysrEgG1s0MDszMG3eG1s0NzszN22xstvbG1swbd8gG1sxbbAbWzQ3bdvb3RtbMG0gG1szNG3+IBtbMTszN22wG1s0N22y29vb29vb27KxG1swbSAbWzM0bdwbWzFt3BtbMDszNm3cG1sxOzM0bdwbWzA7MzRt3NwbWzFt3BtbMDszNG3fDQog3htbNDQ7MzZtsBtbNDY7MzRtshtbMTs0MG3d3t/fG1swbSDc3NwgG1szNG3cG1szN20g3tsbWzE7NDdt3BtbMG3bG1sxbbEbWzQ3bbLb29wbWzBt3N4bWzE7NDdt29uyG1swbd0bWzE7NDdtsbLb29vb3BtbMG3cG1sxOzQ3bbKxsBtbMG3bG1sxOzMwbd4bWzQ3OzM3bbCy27EbWzBtIBtbMzRtINwbWzE7MzBt3xtbMG3fG1sxbd/fG1szMG0gG1swbSDeG1sxOzQ3bbCysRtbMG3dICDbG1sxOzQ3bbKyshtbMG3dIBtbMzRt3xtbMW3f3BtbNDZtshtbMG0NChtbMzRtICDfG1sxbdsbWzQ2bd+yG1s0MG3dG1swbbAbWzQ3OzMwbbAbWzQwOzM3bdsbWzFtsRtbMG3c3NzcG1sxOzQ3bSAbWzQwbbGxG1s0N20gG1swbdwbWzE7NDc7MzBt3BtbNDA7Mzdt3xtbNDdt39/b29vbsRtbMG0gG1sxOzQ3bbCxsbAbWzBtIBtbMW3f2xtbNDdt29uysBtbMG3bIBtbMTs0NzszMG3cG1szN22wsbAbWzBt29wgG1szNG3f3xtbMzdtINyxG1sxOzMwbd0bWzBtINsbWzE7NDdtsbEbWzBt3RtbMzRt3twbWzM3bd4bWzE7NDdtsbGxG1swbdsgG1sxOzQ0OzM2bbAbWzQwOzM0bd/fDQobWzBtG1s1QxtbMTszNG0gG1swOzM0bSAbWzM3bbAbWzQ3OzMwbbAbWzQwOzM3bdvb2xtbMTs0N23bsrGwsCAbWzBt29vbG1sxOzQ3OzMwbd8bWzQwbdwbWzBtIN4bWzE7NDdtsrKyG1swbd3e2xtbMTs0N22wsBtbMG3b3SAgG1sxbd/bG1s0N23bsbAbWzBt3SDfG1sxOzQ3bSAbWzQwbbGxG1s0N20g3BtbMG3bG1sxOzQ3bf4bWzBt29uyG1sxbd0bWzA7MzRt3BtbMzdt3htbMTs0N22wG1swbdsbWzFt3RtbMzRt3htbMDs0NDszNm2wG1s0MDszN23eG1sxOzQ3bbCwsBtbMG3bG1sxbd0bWzA7MzZt3t0NChtbMzdtICAgIBtbMzZtsBtbNDY7MzBtshtbNDA7MzdtICAbWzFt3xtbNDdt3NzcG1s0MG2yG1swbd3f39/fIBtbMTszNG3c3BtbMDszNG3c3BtbMzdtINsbWzE7NDdtsLCwG1swbSAbWzFt39/fG1s0N23c3NwbWzQwbd0bWzA7MzRt3xtbMW3cG1swbSAbWzFt3xtbNDdtsrEbWzQwbbEbWzBt3CAbWzM0bdwbWzM3bSDf39vbG1s0NzszMG2wG1s0MDszN22yG1sxbd/fG1swbSAg3tsbWzFt3xtbMDszNG0gG1s0NDszNm2yG1s0MDszN20gG1sxbbAbWzBt29sbWzQ3OzMwbbAbWzE7NDA7MzdtsRtbMG0gG1sxOzM2bd8NChtbMG0bWzZDG1szNm3fG1szN20gIBtbMzZt3BtbMzdtICAbWzM2bdzcG1sxbdwbWzA7MzZt39/bG1sxbbEbWzQ0bbAbWzA7NDQ7MzZtsBtbNDA7MzRtst8bWzM3bd4bWzE7NDc7MzNtIBtbMG3bG1sxOzQ3OzMzbbAbWzQwbd0bWzMwbbAbWzBtsBtbNDc7MzBtshtbNDA7MzdtICAbWzM0bdwbWzM3bSAbWzFt3xtbMG0gG1sxOzM2bbAbWzQ2OzM0bd8bWzQwbdzcG1swbSAbWzFt39/f3NwbWzBtIBtbMzRt3yDc3NwgG1szN20gG1szNG0gG1sxOzM3bdwbWzBt3xtbMW3fG1swbSAbWzE7MzRt3htbMG0g39/fG1sxbd/fDQobWzBtG1sxM0MbWzM0bSAbWzM3bSAgG1szNG2wG1szN20gG1szNG2wG1s0NDszMG2yG1s0MDszN20gIBtbMW3cG1s0N23cG1szM23c27LbG1s0MDszN23b29sbWzMzbd8bWzBtIBtbMzRtICDf3xtbNDQ7MzZtIN/fsBtbNDA7MzRt2xtbMW3f3BtbMG0bWzVD/iAgICDfG1s1QxtbMzRtIP4NChtbMzdtG1syNkMbWzE7MzNt3Nvb3xtbMG0bWzEyQxtbMzRtIN4bWzFt3bAbWzA7NDQ7MzBtshtbNDA7MzdtIBtbMTszMG0gIBtbMzNtYmJzG1swbSBzb2Z0d2FyZQ0KG1syNUMbWzE7MzNt3NsbWzM3bd8bWzBtG1sxMkMbWzM0bdwbWzM3bSAgG1szNG3fDQobWzM3bRtbMjRDG1sxOzM2bdwbWzM3bdsbWzMzbdzcDQobWzBtG1syNUMbWzE7MzNt3N8NChtbMG0bWzI1QxtbMTszM23dDQo=';
     if (settings && settings.ftelnet_splash) {
         var f = new File(settings.ftelnet_splash);
@@ -9,4 +11,18 @@ function getSplash() {
     } else {
         return DefaultSplash;
     }
-}
\ No newline at end of file
+}
+
+function get_url() {
+    try {
+        const str = (new HTTPRequest()).Get('http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime());
+        const match = str.match(/^.+\ssrc="(.*)"\s.+/);
+        if (match !== null) return match[1];
+        throw 'Failed to fetch fTelnet version';
+    } catch (err) {
+        log(LOG_ERR, err);
+    }
+    return 'http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=2018-09-14';
+}
+
+this;
-- 
GitLab


From 99a1e099f3fd597b935877f27f868859594ab174 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Jan 2019 10:10:10 -0500
Subject: [PATCH 402/752] Use 'locale' object instead of previous language
 thing. Return-style load of web/lib/ftelnet.js. Use get_url to fetch ftelnet
 script src, avoids browser console log output about parser-blocking
 document.write blah blah. (I should probably just cache this URL since it
 doesn't change often.  I'm sure a daily refresh would be more than enough.)
 Allow for 'wsp' and 'wssp' values from modopts.ini [web], which can override
 automatic lookup of WS and WSS service ports if you need to do that. You'll
 see a mixed-content error in the browser console re: myip.randm.ca; this
 doesn't seem to be a problem but probably needs to be fixed in ftelnet.

---
 web/pages/.examples/000-home.xjs | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/web/pages/.examples/000-home.xjs b/web/pages/.examples/000-home.xjs
index 6fcb37c903..e6af12b809 100644
--- a/web/pages/.examples/000-home.xjs
+++ b/web/pages/.examples/000-home.xjs
@@ -1,10 +1,9 @@
 <!--Home-->
-<?xjs var _hpl = getLanguage(settings.language_file || 'english.ini', 'page_home'); ?>
 
 <!-- fTelnet -->
 <?xjs
     if (settings.ftelnet) {
-        load(settings.web_lib + 'ftelnet.js');
+        const ftelnet_lib = load({}, settings.web_lib + 'ftelnet.js');
         load('ftelnethelper.js');
 ?>
         <style>.fTelnetStatusBar { display : none; }</style>
@@ -12,15 +11,15 @@
         <div class="row">
             <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
                 <button id="ftelnet-connect" class="btn btn-primary">
-                    <?xjs write(_hpl.button_ftelnet); ?>
+                    <?xjs write(locale.strings.page_home.button_ftelnet); ?>
                 </button>
             </div>
         </div>
 
-    	<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
+    	<script id="fTelnetScript" src="<?xjs write(ftelnet_lib.get_url()); ?>"></script>
     	<script>
-            var wsp = <?xjs write(GetWebSocketServicePort()); ?>;
-            var wssp = <?xjs write(GetWebSocketServicePort(true)); ?>;
+            var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
+            var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;
     		var Options = new fTelnetOptions();
     		Options.BareLFtoCRLF = false;
     		Options.BitsPerSecond = 57600;
@@ -34,9 +33,11 @@
     		Options.Port = location.protocol == 'https:' ? wssp : wsp;
     		Options.ScreenColumns = 80;
     		Options.ScreenRows = 25;
-            Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+            Options.SplashScreen = '<?xjs write(ftelnet_lib.get_splash()); ?>';
     		var fTelnet = new fTelnetClient('fTelnetContainer', Options);
-            $('#ftelnet-connect').click(function() { fTelnet.Connect(); });
+            $('#ftelnet-connect').click(function() {
+                fTelnet.Connect();
+            });
     	</script>
 
 <?xjs } ?>
-- 
GitLab


From c49e34976f999b5a269cf72fec062a150a2ab8cc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Jan 2019 12:45:34 -0500
Subject: [PATCH 403/752] fTelnet script src cache, refresh if > 24 hours old.

---
 web/lib/ftelnet.js | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index 9d32c8e971..0d2a8ffcee 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -13,16 +13,35 @@ function get_splash() {
     }
 }
 
+function get_cached_url(f) {
+    if (f.open('r')) {
+        const ret = f.read();
+        f.close();
+        return ret;
+    }
+    return 'http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=2018-09-14';
+}
+
 function get_url() {
-    try {
-        const str = (new HTTPRequest()).Get('http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime());
+
+    const f = new File(system.temp_dir + 'ftelnet.url');
+    if (!f.exists || time() - f.date > 86400) {
+        try {
+            var str = (new HTTPRequest()).Get('http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime());
+        } catch (err) {
+            log(LOG_ERR, 'Failed to fetch fTelnet URL');
+            str = '';
+        }
         const match = str.match(/^.+\ssrc="(.*)"\s.+/);
-        if (match !== null) return match[1];
-        throw 'Failed to fetch fTelnet version';
-    } catch (err) {
-        log(LOG_ERR, err);
+        if (match !== null) {
+            if (f.open('w')) {
+                f.write(match[1]);
+                f.close();
+            }
+            return match[1];
+        }
     }
-    return 'http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=2018-09-14';
+    return get_cached_url(f);
 }
 
 this;
-- 
GitLab


From 92fe9267196f05266592e715ac231246a7372780 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Jan 2019 23:24:39 -0500
Subject: [PATCH 404/752] Use settings[wsp,wssp] if applicable, use
 return-style ftelnet lib.

---
 web/pages/.examples/003-games.xjs | 137 ++++++++++++++----------------
 1 file changed, 62 insertions(+), 75 deletions(-)

diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index 535593ae83..bcc8548b5c 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -2,34 +2,26 @@
 <?xjs
 
     load("ftelnethelper.js");
-	load(settings.web_lib + 'ftelnet.js');
+    const ftelnet_lib = load({}, settings.web_lib + 'ftelnet.js');
 
-	if (typeof settings.xtrn_blacklist === 'string') {
-		settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(',');
-	} else {
-		settings.xtrn_blacklist = [];
-	}
+    if (typeof settings.xtrn_blacklist === 'string') {
+        settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(',');
+    } else {
+        settings.xtrn_blacklist = [];
+    }
 
-	var xtrn = {};
-	xtrn_area.sec_list.forEach(
-		function (sec) {
-			if (!sec.can_access || sec.prog_list.length < 1) return;
-			if (settings.xtrn_blacklist.indexOf(sec.code.toLowerCase()) > -1) {
-				return;
-			}
-			var x = [];
-			sec.prog_list.forEach(
-				function (prog) {
-					if (!prog.can_access || !prog.can_run) return;
-					if (settings.xtrn_blacklist.indexOf(prog.code.toLowerCase()) > -1) {
-						return;
-					}
-					x.push({ c : prog.code, n : prog.name });
-				}
-			);
-			if (x.length > 0) xtrn[sec.name] = x;
-		}
-	);
+    var xtrn = {};
+    xtrn_area.sec_list.forEach(function (sec) {
+        if (!sec.can_access || sec.prog_list.length < 1) return;
+        if (settings.xtrn_blacklist.indexOf(sec.code.toLowerCase()) > -1) return;
+        var x = [];
+        sec.prog_list.forEach(function (prog) {
+            if (!prog.can_access || !prog.can_run) return;
+            if (settings.xtrn_blacklist.indexOf(prog.code.toLowerCase()) > -1) return;
+            x.push({ c : prog.code, n : prog.name });
+        });
+        if (x.length > 0) xtrn[sec.name] = x;
+    });
 
 ?>
 
@@ -51,58 +43,53 @@
 
 <script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
 <script type="text/javascript">
-    var wsp = <?xjs write(GetWebSocketServicePort()); ?>;
-    var wssp = <?xjs write(GetWebSocketServicePort(true)); ?>;
-	var Options = new fTelnetOptions();
-	Options.BareLFtoCRLF = false;
-	Options.BitsPerSecond = 57600;
-	Options.ConnectionType = 'rlogin';
-	Options.Emulation = 'ansi-bbs';
-	Options.Enter = '\r';
-	Options.Font = 'CP437';
-	Options.ForceWss = false;
-	Options.Hostname = '<?xjs write(http_request.vhost); ?>';
-	Options.LocalEcho = false;
+    var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
+    var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;
+    var Options = new fTelnetOptions();
+    Options.BareLFtoCRLF = false;
+    Options.BitsPerSecond = 57600;
+    Options.ConnectionType = 'rlogin';
+    Options.Emulation = 'ansi-bbs';
+    Options.Enter = '\r';
+    Options.Font = 'CP437';
+    Options.ForceWss = false;
+    Options.Hostname = '<?xjs write(http_request.vhost); ?>';
+    Options.LocalEcho = false;
     Options.Port = location.protocol == 'https:' ? wssp : wsp;
-	Options.RLoginClientUsername = '<?xjs write(user.security.password); ?>';
-	Options.RLoginServerUsername = '<?xjs write(user.alias); ?>';
-	Options.ScreenColumns = 80;
-	Options.ScreenRows = 25;
-    Options.SplashScreen = Options.SplashScreen = '<?xjs write(getSplash()); ?>';
+    Options.RLoginClientUsername = '<?xjs write(user.security.password); ?>';
+    Options.RLoginServerUsername = '<?xjs write(user.alias); ?>';
+    Options.ScreenColumns = 80;
+    Options.ScreenRows = 25;
+    Options.SplashScreen = Options.SplashScreen = '<?xjs write(ftelnet_lib.get_splash()); ?>';
     Options.WebSocketUrlPath = '?Port=<?xjs write(GetRLoginPort()); ?>';
-	var fTelnet = new fTelnetClient('fTelnetContainer', Options);
-	fTelnet.OnConnectionClose = function () { window.location.reload(); };
+    var fTelnet = new fTelnetClient('fTelnetContainer', Options);
+    fTelnet.OnConnectionClose = function () {
+        window.location.reload();
+    };
 
-	function launchXtrn(code) {
-		$.getJSON(
-			'./api/system.ssjs?call=set-xtrn-intent&code=' + code,
-			function(data) {
-				fTelnet._Options.RLoginTerminalType = 'xtrn=' + code;
-                fTelnet.Connect();
-            }
-		);
-	}
+    function launchXtrn(code) {
+        $.getJSON('./api/system.ssjs?call=set-xtrn-intent&code=' + code, function(data) {
+            fTelnet._Options.RLoginTerminalType = 'xtrn=' + code;
+            fTelnet.Connect();
+        });
+    }
 
-	var xtrn = <?xjs write(JSON.stringify(xtrn)); ?>;
-	Object.keys(xtrn).forEach(
-		function (x) {
-			var e = $('#xtrn-list-template').clone();
-			$($(e).find('h4')[0]).text(x);
-			var ul = $($(e).find('ul')[0]);
-			xtrn[x].forEach(
-				function (xx) {
-					var li = $('#xtrn-item-template').clone();
-					var a = $(li).find('a')[0];
-					$(a).text(xx.n);
-					$(a).click(function () { launchXtrn(xx.c); });
-					$(ul).append(li);
-					$(li).show();
-				}
-			);
-			$(e).append(ul);
-			$('#xtrn-list').append(e);
-			$(e).show();
-		}
-	);
+    var xtrn = <?xjs write(JSON.stringify(xtrn)); ?>;
+    Object.keys(xtrn).forEach(function (x) {
+        var e = $('#xtrn-list-template').clone();
+        $($(e).find('h4')[0]).text(x);
+        var ul = $($(e).find('ul')[0]);
+        xtrn[x].forEach(function (xx) {
+            var li = $('#xtrn-item-template').clone();
+            var a = $(li).find('a')[0];
+            $(a).text(xx.n);
+            $(a).click(function () { launchXtrn(xx.c); });
+            $(ul).append(li);
+            $(li).show();
+        });
+        $(e).append(ul);
+        $('#xtrn-list').append(e);
+        $(e).show();
+    });
 
 </script>
-- 
GitLab


From 9920e8b28f187933956936579e41bcf0327b7f99 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 12 Jan 2019 23:37:14 -0500
Subject: [PATCH 405/752] Use locale.

---
 web/pages/.examples/000-mail.xjs | 89 +++++++++++++++-----------------
 1 file changed, 42 insertions(+), 47 deletions(-)

diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index c8385dc9e9..77f6d96f76 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -1,20 +1,16 @@
 <!--HIDDEN:Mail-->
 
 <?xjs
+    if (user.number == 0 || user.alias == settings.guest) exit();
 
-if (user.number == 0 || user.alias == settings.guest) exit();
-
-load('sbbsdefs.js');
-load(settings.web_lib + 'forum.js');
-
-var _mpl = getLanguage(settings.language_file || 'english.ini', 'page_mail');
-
-function sent() {
-	if (typeof http_request.query.sent === 'undefined') return false;
-	if (http_request.query.sent[0] == '0') return false;
-	return true;
-}
+    load('sbbsdefs.js');
+    load(settings.web_lib + 'forum.js');
 
+    function sent() {
+    	if (typeof http_request.query.sent === 'undefined') return false;
+    	if (http_request.query.sent[0] == '0') return false;
+    	return true;
+    }
 ?>
 
 <?xjs function writeMessage(header) { ?>
@@ -28,12 +24,12 @@ function sent() {
 				</div>
 			</div>
 			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
-				<?xjs write(!sent() ? _mpl.label_message_from : _mpl.label_message_to); ?>:
+				<?xjs write(!sent() ? locale.strings.page_mail.label_message_from : locale.strings.page_mail.label_message_to); ?>:
 				<strong><?xjs write(!sent() ? header.from : header.to); ?></strong>
-				<?xjs write(_mpl.label_message_date); ?>
+				<?xjs write(locale.strings.page_mail.label_message_date); ?>
 				<?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
 				<p>
-					<?xjs write(_mpl.label_message_subject); ?>:
+					<?xjs write(locale.strings.page_mail.label_message_subject); ?>:
 					<strong><?xjs write(header.subject); ?></strong>
 				</p>
 			</div>
@@ -46,47 +42,46 @@ function sent() {
 
 <script type="text/javascript">
 
-	function selectAllMail() {
-		$('input.mail-select').each(
-			function () {
-				$(this).prop('checked', $(this).prop('checked') ? false : true);
-			}
-		);
-	}
-
-	function deleteSelectedMail() {
-		var numbers = [];
-		$('input.mail-select:checked').each(
-			function () { numbers.push($(this).attr('id').split('-')[1]); }
-		);
-		$.getJSON(
-			'./api/forum.ssjs?call=delete-mail&number=' + numbers.join('&number='),
-			function (data) {
-				if (!data.success) return;
-				numbers.forEach(function (e) { $('#li-' + e).remove(); });
-			}
-		);
-	}
+    function selectAllMail() {
+        $('input.mail-select').each(function () {
+            $(this).prop('checked', $(this).prop('checked') ? false : true);
+        });
+    }
+
+    function deleteSelectedMail() {
+        var numbers = [];
+        $('input.mail-select:checked').each(function () {
+            numbers.push($(this).attr('id').split('-')[1]);
+        });
+        $.getJSON('./api/forum.ssjs?call=delete-mail&number=' + numbers.join('&number='), function (data) {
+            if (!data.success) return;
+            numbers.forEach(function (e) {
+                $('#li-' + e).remove();
+            });
+        });
+    }
 
 </script>
 
 <?xjs if (typeof http_request.query.notice != "undefined") { ?>
-	<div id="noticebox" class="alert alert-warning">
-		<?xjs write(http_request.query.notice[0]); ?>
-	</div>
-	<script type="text/javascript">
-		$("#noticebox").fadeOut(3000, function () { $("#noticebox").remove(); });
-	</script>
+    <div id="noticebox" class="alert alert-warning">
+        <?xjs write(http_request.query.notice[0]); ?>
+    </div>
+    <script type="text/javascript">
+        $("#noticebox").fadeOut(3000, function () {
+            $("#noticebox").remove();
+        });
+    </script>
 <?xjs } ?>
 
 <?xjs if (!(user.security.restrictions&UFLAG_E) && !(user.security.restrictions&UFLAG_M)) { ?>
-	<button class="btn btn-default icon" aria-label="<?xjs write(_mpl.button_post_new); ?>" title="<?xjs write(_mpl.button_post_new); ?>" onclick="addNew('mail')">
+	<button class="btn btn-default icon" aria-label="<?xjs write(locale.strings.page_mail.button_post_new); ?>" title="<?xjs write(locale.strings.page_mail.button_post_new); ?>" onclick="addNew('mail')">
 		<span class="glyphicon glyphicon-pencil"></span>
 	</button>
-	<button class="btn btn-default icon" aria-label="<?xjs write(_mpl.button_select_all); ?>" title="<?xjs write(_mpl.button_select_all); ?>" onclick="selectAllMail()">
+	<button class="btn btn-default icon" aria-label="<?xjs write(locale.strings.page_mail.button_select_all); ?>" title="<?xjs write(locale.strings.page_mail.button_select_all); ?>" onclick="selectAllMail()">
 		<span class="glyphicon glyphicon-check"></span>
 	</button>
-	<button class="btn btn-default icon" aria-label="<?xjs write(_mpl.button_delete_selected); ?>" title="<?xjs write(_mpl.button_delete_selected); ?>" onclick="deleteSelectedMail()">
+	<button class="btn btn-default icon" aria-label="<?xjs write(locale.strings.page_mail.button_delete_selected); ?>" title="<?xjs write(locale.strings.page_mail.button_delete_selected); ?>" onclick="deleteSelectedMail()">
 		<span class="glyphicon glyphicon-trash"></span>
 	</button>
 <?xjs } ?>
@@ -94,12 +89,12 @@ function sent() {
 <ul class="nav nav-tabs">
 	<li role="presentation" class="<?xjs write(!sent() ? ' active' : ''); ?>">
 		<a href="./?page=<?xjs write(page); ?>&amp;sent=0">
-			<?xjs write(_mpl.label_tab_inbox); ?>
+			<?xjs write(locale.strings.page_mail.label_tab_inbox); ?>
 		</a>
 	</li>
 	<li role="presentation" class="<?xjs write(sent() ? ' active' : ''); ?>">
 		<a href="./?page=<?xjs write(page); ?>&amp;sent=1">
-			<?xjs write(_mpl.label_tab_sent); ?>
+			<?xjs write(locale.strings.page_mail.label_tab_sent); ?>
 		</a>
 	</li>
 </ul>
-- 
GitLab


From fb5b627d020d2f9905a3c1b33da4af7a8ae54f73 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 13 Jan 2019 00:49:27 -0500
Subject: [PATCH 406/752] Use locale.

---
 web/pages/.examples/000-register.xjs | 114 +++++++++++++--------------
 1 file changed, 56 insertions(+), 58 deletions(-)

diff --git a/web/pages/.examples/000-register.xjs b/web/pages/.examples/000-register.xjs
index b719af918b..5853d8fed0 100644
--- a/web/pages/.examples/000-register.xjs
+++ b/web/pages/.examples/000-register.xjs
@@ -12,21 +12,19 @@
 
 	load('sbbsdefs.js');
 
-	var _rpl = getLanguage(settings.language_file || 'english.ini', 'page_register');
-
 	function required(mask) {
-		return ((system.new_user_questions&mask) ? (' ' + _rpl.stat_suffix_field_required) : '');
+		return ((system.new_user_questions&mask) ? (' ' + locale.strings.page_register.stat_suffix_field_required) : '');
 	}
 
 	function notRequired(mask) {
-		return ((system.new_user_questions&mask) ? '' : (' ' + _rpl.stat_suffix_field_required));
+		return ((system.new_user_questions&mask) ? '' : (' ' + locale.strings.page_register.stat_suffix_field_required));
 	}
 
 
 	function iconRequired(mask) {
 		if (required(mask).length > 0) {
 ?>
-			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
 <?xjs
 		}
 	}
@@ -34,7 +32,7 @@
 	function iconNotRequired(mask) {
 		if (notRequired(mask).length > 0) {
 ?>
-			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
 <?xjs
 		}
 	}
@@ -42,137 +40,137 @@
 
 <script type="text/javascript" src="./js/validator.js"></script>
 
-<div class="well well-sm"><h3><?xjs write(_rpl.title); ?></h3></div>
+<div class="well well-sm"><h3><?xjs write(locale.strings.page_register.title); ?></h3></div>
 
 <div id="errorbox" class="bg-danger" hidden></div>
 
 <div id="form-register-container">
 
 	<div style="margin-bottom:1em;">
-		<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
-		<?xjs write(_rpl.help_text_required); ?>
+		<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+		<?xjs write(locale.strings.page_register.help_text_required); ?>
 	</div>
 
 	<form id="form-register" data-toggle="validator">
 
 		<div class="form-group">
-			<label for="alias"><?xjs write(_rpl.input_alias); ?></label> 
-			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
-			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_ALIAS); ?>" class="form-control" id="alias" name="alias" placeholder="<?xjs write(_rpl.input_alias); ?>" required>
+			<label for="alias"><?xjs write(locale.strings.page_register.input_alias); ?></label>
+			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_ALIAS); ?>" class="form-control" id="alias" name="alias" placeholder="<?xjs write(locale.strings.page_register.input_alias); ?>" required>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_ALIAS)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_ALIAS)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
 			<label for="password1" class="control-label">
-				<?xjs write(_rpl.input_password); ?>
-			</label> 
-			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
-			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="<?xjs write(_rpl.input_password); ?>" required>
+				<?xjs write(locale.strings.page_register.input_password); ?>
+			</label>
+			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="<?xjs write(locale.strings.page_register.input_password); ?>" required>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_minimum_characters, settings.minimum_password_length)); ?>,
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_PASS)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_minimum_characters, settings.minimum_password_length)); ?>,
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_PASS)); ?>
 			</span>
 		</div>
 			<div class="form-group">
 			<label for="password2" class="control-label">
-				<?xjs write(_rpl.input_password_confirm); ?>
-			</label> 
-			<span title="<?xjs write(_rpl.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span> 
-			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="<?xjs write(_rpl.input_password_confirm); ?>" data-match="#password1" required>
+				<?xjs write(locale.strings.page_register.input_password_confirm); ?>
+			</label>
+			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
+			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="<?xjs write(locale.strings.page_register.input_password_confirm); ?>" data-match="#password1" required>
 		</div>
 
 		<div class="form-group">
-			<label for="netmail"><?xjs write(_rpl.input_email); ?></label> 
+			<label for="netmail"><?xjs write(locale.strings.page_register.input_email); ?></label>
 			<?xjs iconNotRequired(UQ_NONETMAIL); ?>
-			<input type="email" data-minlength="6" maxlength="<?xjs write(LEN_NETMAIL); ?>"class="form-control" id="netmail" name="netmail" placeholder="<?xjs write(_rpl.placeholder_netmail); ?>"<?xjs write(notRequired(UQ_NONETMAIL)); ?>>
+			<input type="email" data-minlength="6" maxlength="<?xjs write(LEN_NETMAIL); ?>"class="form-control" id="netmail" name="netmail" placeholder="<?xjs write(locale.strings.page_register.placeholder_netmail); ?>"<?xjs write(notRequired(UQ_NONETMAIL)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_NETMAIL)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_NETMAIL)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="realname"><?xjs write(_rpl.input_name); ?></label> 
+			<label for="realname"><?xjs write(locale.strings.page_register.input_name); ?></label>
 			<?xjs iconRequired(UQ_REALNAME); ?>
-			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_NAME); ?>" class="form-control" id="realname" name="realname" placeholder="<?xjs write(_rpl.placeholder_name); ?>"<?xjs write(required(UQ_REALNAME)); ?>>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_NAME); ?>" class="form-control" id="realname" name="realname" placeholder="<?xjs write(locale.strings.page_register.placeholder_name); ?>"<?xjs write(required(UQ_REALNAME)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_NAME)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_NAME)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
 			<label for="address">
-				<?xjs write(_rpl.input_street_address); ?>
-			</label> 
+				<?xjs write(locale.strings.page_register.input_street_address); ?>
+			</label>
 			<?xjs iconRequired(UQ_ADDRESS); ?>
-			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="address" name="address" placeholder="<?xjs write(_rpl.placeholder_street_address); ?>"<?xjs write(required(UQ_ADDRESS)); ?>>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="address" name="address" placeholder="<?xjs write(locale.strings.page_register.placeholder_street_address); ?>"<?xjs write(required(UQ_ADDRESS)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_ADDRESS)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_ADDRESS)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="zipcode"><?xjs write(_rpl.input_zipcode); ?></label> 
+			<label for="zipcode"><?xjs write(locale.strings.page_register.input_zipcode); ?></label>
 			<?xjs iconRequired(UQ_ADDRESS); ?>
-			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="zipcode" name="zipcode" placeholder="<?xjs write(_rpl.placeholder_zipcode); ?>"<?xjs write(required(UQ_ADDRESS)); ?>>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_ADDRESS); ?>" class="form-control" id="zipcode" name="zipcode" placeholder="<?xjs write(locale.strings.page_register.placeholder_zipcode); ?>"<?xjs write(required(UQ_ADDRESS)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_ADDRESS)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_ADDRESS)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="location"><?xjs write(_rpl.input_location); ?></label> 
+			<label for="location"><?xjs write(locale.strings.page_register.input_location); ?></label>
 			<?xjs iconRequired(UQ_LOCATION); ?>
-			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_LOCATION); ?>" class="form-control" id="location" name="location" placeholder="<?xjs write(_rpl.placeholder_location); ?>"<?xjs write(required(UQ_LOCATION)); ?>>
+			<input type="text" data-minlength="1" maxlength="<?xjs write(LEN_LOCATION); ?>" class="form-control" id="location" name="location" placeholder="<?xjs write(locale.strings.page_register.placeholder_location); ?>"<?xjs write(required(UQ_LOCATION)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_LOCATION)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_LOCATION)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="phone"><?xjs write(_rpl.input_phone); ?></label> 
+			<label for="phone"><?xjs write(locale.strings.page_register.input_phone); ?></label>
 			<?xjs iconRequired(UQ_PHONE); ?>
-			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_PHONE); ?>" class="form-control" id="phone" name="phone" placeholder="<?xjs write(_rpl.placeholder_phone); ?>"<?xjs write(required(UQ_PHONE)); ?>>
+			<input type="text" data-minlength="3" maxlength="<?xjs write(LEN_PHONE); ?>" class="form-control" id="phone" name="phone" placeholder="<?xjs write(locale.strings.page_register.placeholder_phone); ?>"<?xjs write(required(UQ_PHONE)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_PHONE)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_PHONE)); ?>
 			</span>
 		</div>
 
 		<div class="form-group">
-			<label for="birth"><?xjs write(_rpl.input_birthdate); ?></label> 
+			<label for="birth"><?xjs write(locale.strings.page_register.input_birthdate); ?></label>
 			<?xjs iconRequired(UQ_BIRTH); ?>
 			<input type="text" data-minlength="<?xjs write(LEN_BIRTH); ?>" maxlength="<?xjs write(LEN_BIRTH); ?>" class="form-control" id="birth" name="birth" placeholder="<?xjs write(system.settings&SYS_EURODATE ? 'DD/MM/YY' : 'MM/DD/YY'); ?>" <?xjs write(required(UQ_BIRTH)); ?>>
 			<span class="help-block">
-				<?xjs write(format(_rpl.help_text_maximum_characters, LEN_BIRTH)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_BIRTH)); ?>
 			</span>
 		</div>
 
 		<div class="form-inline">
-			<?xjs iconRequired(UQ_SEX); ?> 
-			<label><?xjs write(_rpl.input_gender); ?></label>&nbsp;
+			<?xjs iconRequired(UQ_SEX); ?>
+			<label><?xjs write(locale.strings.page_register.input_gender); ?></label>&nbsp;
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-withheld" name="gender" value="X" checked> 
-					<?xjs write(_rpl.input_gender_withheld); ?> 
+					<input type="radio" id="gender-withheld" name="gender" value="X" checked>
+					<?xjs write(locale.strings.page_register.input_gender_withheld); ?>
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-male" name="gender" value="M"> 
-					<?xjs write(_rpl.input_gender_male); ?>
+					<input type="radio" id="gender-male" name="gender" value="M">
+					<?xjs write(locale.strings.page_register.input_gender_male); ?>
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-female" name="gender" value="F"> 
-					<?xjs write(_rpl.input_gender_female); ?>
+					<input type="radio" id="gender-female" name="gender" value="F">
+					<?xjs write(locale.strings.page_register.input_gender_female); ?>
 				</label>
 			</div>
 			<div class="radio">
 				<label>
-					<input type="radio" id="gender-other" name="gender" value="O"> 
-					<?xjs write(_rpl.input_gender_other); ?>
+					<input type="radio" id="gender-other" name="gender" value="O">
+					<?xjs write(locale.strings.page_register.input_gender_other); ?>
 				</label>
 			</div>
 		</div>
@@ -181,7 +179,7 @@
 		<?xjs if(system.newuser_password !== '') { ?>
 		<div class="form-group">
 			<label for="newuser-password">
-				<?xjs write(_rpl.input_registration_password); ?>
+				<?xjs write(locale.strings.page_register.input_registration_password); ?>
 			</label>
 			<input type="password" id="newuser-password" name="newuser-password" data-minlength="1" maxlength="8">
 		</div>
@@ -193,7 +191,7 @@
 
 		<div class="pull-right">
 			<button class="btn btn-primary" type="submit">
-				<?xjs write(_rpl.button_register); ?>
+				<?xjs write(locale.strings.page_register.button_register); ?>
 			</button>
 		</div>
 
@@ -220,7 +218,7 @@
 		 		} else {
 		 			$('#errorbox').attr('hidden', true);
 		 			$('#form-register-container').html(
-		 				'<?xjs write(_rpl.message_account_created); ?>'
+		 				'<?xjs write(locale.strings.page_register.message_account_created); ?>'
 		 			);
 		 		}
 		 	}
@@ -240,4 +238,4 @@
 	$('#send-me-free-stuff').attr('hidden', true);
 	$('#subscribe-to-newsletter').attr('hidden', true);
 
-</script>
\ No newline at end of file
+</script>
-- 
GitLab


From daf51053a890f9c722e83369ae6712661432f5aa Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Jan 2019 15:02:57 -0800
Subject: [PATCH 407/752] Use "Web" rather "HTTP" for the connection/protocol
 of web users. Why? Well, it was misleading when the user was actually using
 HTTPS and I think "Web" is purdier. :-P

---
 web/lib/events/nodelist.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index f86978fded..a3f931f61e 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -94,7 +94,7 @@ function scan() {
                 node: 'W',
                 action: locale.strings.sidebar_node_list.label_status_web + ' ' + web_state[e],
                 user: usr.alias,
-                connection: 'HTTP'
+                connection: 'Web'
             };
         }));
     }
-- 
GitLab


From 2236f91cfbb819f142b46dbf647a7e469250e5cd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Jan 2019 15:56:20 -0800
Subject: [PATCH 408/752] Bug-fix: msg header 'from_ext' properties were not
 being propagated to the pages scripts, so local avatars were not displayed
 when viewing locally-posted messages. Not sure what other problems the
 absence of this header field may have caused.

---
 web/lib/forum.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 1b50fd66c6..8680eddebc 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -879,6 +879,7 @@ function getMessageThreads(sub, max) {
             auxattr : header.auxattr,
             number : header.number,
             from : header.from,
+			from_ext : header.from_ext,
             from_net_addr : header.from_net_addr,
             to : header.to,
             when_written_time : header.when_written_time,
-- 
GitLab


From dd75fdd8039475a71f71bb79a110a948597e56bf Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 17:38:47 -0500
Subject: [PATCH 409/752] Attempt to get plaintext body from MsgBase (doesn't
 seem to work). Looks like I went on a formatting spree at some point.

---
 web/lib/forum.js     |  70 ++----
 web/root/js/forum.js | 550 ++++++++++++++++++-------------------------
 2 files changed, 252 insertions(+), 368 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 8680eddebc..c028d72a93 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,7 +1,3 @@
-load('sbbsdefs.js');
-load('modopts.js');
-
-load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'mime-decode.js');
 
 function listGroups() {
@@ -272,36 +268,26 @@ function getMailHeaders(sent, ascending) {
 }
 
 function mimeDecode(header, body, code) {
-    var ret = {
+    const ret = {
         type : '',
-        body : [],
-        inlines : [],
-        attachments : []
+        body : []
     };
-    var msg = mime_decode(header, body, code);
-    if (typeof msg.inlines !== 'undefined') {
-        msg.inlines.forEach(
-            function (inline) {
-                ret.inlines.push(
-                    format(
-                        '<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;cid=%s" target="_blank">%s</a>',
-                        code, header.number, inline, inline
-                    )
-                );
-            }
-        );
+    const msg = mime_decode(header, body, code);
+    if (msg.inlines) {
+        ret.inlines = msg.inlines.map(function (e) {
+            return format(
+                '<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;cid=%s" target="_blank">%s</a>',
+                code, header.number, e, e
+            );
+        });
     }
-    if (typeof msg.attachments !== 'undefined') {
-        msg.attachments.forEach(
-            function (attachment) {
-                ret.attachments.push(
-                    format(
-                        '<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;filename=%s" target="_blank">%s</a>',
-                        code, header.number, attachment, attachment
-                    )
-                );
-            }
-        );
+    if (msg.attachments) {
+        ret.attachments = msg.attachments.map(function (e) {
+            return format(
+                '<a href="./api/attachments.ssjs?sub=%s&amp;msg=%s&amp;filename=%s" target="_blank">%s</a>',
+                code, header.number, e, e
+            );
+        });
     }
     ret.type = msg.type;
     ret.body = msg.body;
@@ -312,9 +298,7 @@ function getMailBody(number) {
 
     var ret = {
         type : '',
-        body : '',
-        inlines : [],
-        attachments : []
+        body : ''
     };
 
     number = Number(number);
@@ -323,27 +307,20 @@ function getMailBody(number) {
     var msgBase = new MsgBase('mail');
     if (!msgBase.open()) return ret;
     var header = msgBase.get_msg_header(false, number, false);
-    if (header !== null &&
-        (   header.to_ext == user.number ||
-            header.from_ext == user.number
-        )
-    ) {
-        var body = msgBase.get_msg_body(false, number, header);
+    if (header !== null && (header.to_ext == user.number || header.from_ext == user.number)) {
+        const body = msgBase.get_msg_body(false, number, header);
+        const pt_body = msgBase.get_msg_body(false, number, header, false, false, true, true); // Plaintext body - this doesn't work.
         if (header.to_ext == user.number && (header.attr^MSG_READ)) {
             header.attr|=MSG_READ;
             msgBase.put_msg_header(false, number, header);
         }
     }
     msgBase.close();
-    if (typeof body === 'undefined' || body === null) return ret;
+    if (!body) return ret;
 
     var decoded = mimeDecode(header, body, 'mail');
     ret.type = decoded.type;
-//    if (ret.type == 'html') {
-//      ret.body = html_decode(decoded.body);
-//    } else {
-      ret.body = formatMessage(decoded.body);
-//    }
+    ret.body = formatMessage(pt_body == body ? decoded.body : pt_body); // See above re: pt_body
     ret.inlines = decoded.inlines;
     ret.attachments = decoded.attachments;
 
@@ -879,7 +856,6 @@ function getMessageThreads(sub, max) {
             auxattr : header.auxattr,
             number : header.number,
             from : header.from,
-			from_ext : header.from_ext,
             from_net_addr : header.from_net_addr,
             to : header.to,
             when_written_time : header.when_written_time,
diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index d32cd3e518..31a5aa20ca 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -13,7 +13,7 @@ function insertParam(key, value) {
         }
     }
     if (i<0) kvp[kvp.length] = [key,value].join('=');
-    window.location.search = kvp.join('&'); 
+    window.location.search = kvp.join('&');
 }
 
 // For now we'll just remove nested quotes from the parent post
@@ -35,67 +35,58 @@ function postNew(sub) {
 	var to = $('#newmessage-to').val();
 	var subject = $('#newmessage-subject').val();
 	var body = $('#newmessage-body').val();
-	$.ajax(
-		{	url : './api/forum.ssjs',
-			method : 'POST',
-			data : {
-				call : 'post',
-				sub : sub,
-				to : to,
-				subject : subject,
-				body : body
-			}
+	$.ajax({
+        url: './api/forum.ssjs',
+		method: 'POST',
+		data: {
+			call: 'post',
+			sub: sub,
+			to: to,
+			subject: subject,
+			body: body
 		}
-	).done(
-		function (data) {
-			if (data.success) {
-				$('#newmessage').remove();
-				insertParam('notice', 'Your message has been posted.');
-			}
-			$('#newmessage-button').attr('disabled', false);
+	}).done(function (data) {
+		if (data.success) {
+			$('#newmessage').remove();
+			insertParam('notice', 'Your message has been posted.');
 		}
-	);
+		$('#newmessage-button').attr('disabled', false);
+	});
 }
 
 // (Try to) post a reply to message number 'id' of 'sub' via the web API
 function postReply(sub, id) {
 	$('#reply-button-' + id).attr('disabled', true);
 	var body = $('#replytext-' + id).val();
-	$.ajax(
-		{	url : './api/forum.ssjs',
-			method : 'POST',
-			data : {
-				call : 'post-reply',
-				sub : sub,
-				body : $('#replytext-' + id).val(),
-				pid : id
-			}
+	$.ajax({
+        url: './api/forum.ssjs',
+		method: 'POST',
+		data: {
+			call: 'post-reply',
+			sub: sub,
+			body: $('#replytext-' + id).val(),
+			pid: id
 		}
-	).done(
-		function (data) {
-			if (data.success) {
-				$('#quote-' + id).attr('disabled', false);
-				$('#replybox-' + id).remove();
-				insertParam('notice', 'Your message has been posted.');
-			} else {
-				$('#reply-button-' + id).attr('disabled', false);
-			}
+	}).done(function (data) {
+		if (data.success) {
+			$('#quote-' + id).attr('disabled', false);
+			$('#replybox-' + id).remove();
+			insertParam('notice', 'Your message has been posted.');
+		} else {
+			$('#reply-button-' + id).attr('disabled', false);
 		}
-	);
+	});
 }
 
 // (Try to) delete a message via the web API
 function deleteMessage(sub, id) {
-	$.getJSON(
-		'./api/forum.ssjs?call=delete-message&sub=' + sub + '&number=' + id,
-		function (data) {
-			console.log(data);
-			if (data.success) {
-				$('#li-' + id).remove();
-				insertParam('notice', 'Message deleted.');
-			}
+	$.getJSON('./api/forum.ssjs?call=delete-message&sub=' + sub + '&number=' + id, function (data) {
+		console.log(data);
+		if (data.success) {
+			$('#li-' + id).remove();
+			insertParam('notice', 'Message deleted.');
 		}
-	);
+	});
 }
 
 // Add a new message input form to the element with id 'forum-list-container' for sub 'sub'
@@ -109,18 +100,15 @@ function addNew(sub) {
 		'<input id="newmessage-button" class="btn btn-primary" type="submit" value="Submit" onclick="postNew(\'' + sub + '\')">' +
 		'</li>'
 	);
-	$.getJSON(
-		'./api/forum.ssjs?call=get-signature',
-		function (data) {
-			$('#newmessage-body').val(
-				$('#newmessage-body').val() + '\r\n' + data.signature
-			);
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=get-signature', function (data) {
+		$('#newmessage-body').val(
+            $('#newmessage-body').val() + '\r\n' + data.signature
+		);
+	});
 	window.location.hash = '#newmessage';
-	$('#newmessage-body').keydown(
-		function (evt) { evt.stopImmediatePropagation(); }
-	);
+	$('#newmessage-body').keydown(function (evt) {
+        evt.stopImmediatePropagation();
+    });
 }
 
 function submitPoll(sub) {
@@ -140,21 +128,17 @@ function submitPoll(sub) {
 	if (results < 0 || results > 3) return;
 
 	var answers = [];
-	$('input[name="newpoll-answer-input"]').each(
-		function () {
-			var val = $(this).val();
-			if (val !== '') answers.push(val);
-		}
-	);
+	$('input[name="newpoll-answer-input"]').each(function () {
+		var val = $(this).val();
+		if (val !== '') answers.push(val);
+	});
 	if (answers.length < 1) return;
 
 	var comments = [];
-	$('input[name="newpoll-comment-input"]').each(
-		function () {
-			var val = $(this).val();
-			if (val !== '') comments.push(val);
-		}
-	);
+	$('input[name="newpoll-comment-input"]').each(function () {
+		var val = $(this).val();
+		if (val !== '') comments.push(val);
+	});
 
 	var url =
 		'./api/forum.ssjs?call=submit-poll' +
@@ -166,15 +150,13 @@ function submitPoll(sub) {
 
 	if (comments.length > 0) url += '&comment=' + comments.join('&comment=');
 
-	$.getJSON(
-		url, function (data) {
-			$('#newpoll-submit').attr('disabled', false);
-			if (data.success) {
-				$('#newpoll').remove();
-				insertParam('notice', 'Your poll has been posted.');
-			}
+	$.getJSON(url, function (data) {
+		$('#newpoll-submit').attr('disabled', false);
+		if (data.success) {
+			$('#newpoll').remove();
+			insertParam('notice', 'Your poll has been posted.');
 		}
-	);
+	});
 
 }
 
@@ -202,9 +184,9 @@ function addPollField(type, elem) {
 		'</div>'
 	);
 
-	$('#' + prefix + '-' + number).keydown(
-		function (evt) { evt.stopImmediatePropagation(); }
-	);
+	$('#' + prefix + '-' + number).keydown(function (evt) {
+        evt.stopImmediatePropagation();
+    });
 
 }
 
@@ -286,58 +268,45 @@ function addReply(sub, id) {
 		'<input id="reply-button-' + id + '" class="btn btn-primary" type="submit" value="Submit" onclick="postReply(\'' + sub + '\', ' + id + ')">' +
 		'</div>'
 	);
-	$.getJSON(
-		'./api/forum.ssjs?call=get-signature',
-		function (data) {
-			$('#replytext-' + id).val(
-				$('#replytext-' + id).val() + '\r\n' + data.signature
-			);
-		}
-	);
-	$('#replytext-' + id).keydown(
-		function (evt) {
-			evt.stopImmediatePropagation();
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=get-signature', function (data) {
+		$('#replytext-' + id).val($('#replytext-' + id).val() + '\r\n' + data.signature);
+	});
+	$('#replytext-' + id).keydown(function (evt) {
+		evt.stopImmediatePropagation();
+	});
 }
 
 // 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
 function getSubUnreadCount(sub) {
-	$.getJSON(
-		'./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub,
-		function(data) {
-			for (sub in data) {
-				if (data[sub].scanned > 0) {
-					$('#badge-' + sub).text(data[sub].total);
-				} else if (data[sub].total > 0) {
-					$('#badge-' + sub).text(data[sub].total);
-				} else {
-					$('#badge-' + sub).text('');
-				}
+	$.getJSON('./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub, function (data) {
+		for (sub in data) {
+			if (data[sub].scanned > 0) {
+				$('#badge-' + sub).text(data[sub].total);
+			} else if (data[sub].total > 0) {
+				$('#badge-' + sub).text(data[sub].total);
+			} else {
+				$('#badge-' + sub).text('');
 			}
 		}
-	);
+	});
 }
 
 // 'group' can be a single group index, or a string of 0&group=1&group=2...
 function getGroupUnreadCount(group) {
-	$.getJSON(
-		'./api/forum.ssjs?call=get-group-unread-count&group=' + group,
-		function (data) {
-			for (group in data) {
-				$('#badge-scanned-' + group).text(
-					(data[group].scanned == 0 ? "" : data[group].scanned)
-				);
-				$('#badge-ignored-' + group).text(
-					(	data[group].total == 0 ||
-						data[group].total == data[group].scanned
-						? ''
-						: (data[group].total - data[group].scanned)
-					)
-				);
-			}
+	$.getJSON('./api/forum.ssjs?call=get-group-unread-count&group=' + group, function (data) {
+		for (group in data) {
+			$('#badge-scanned-' + group).text(
+				(data[group].scanned == 0 ? "" : data[group].scanned)
+			);
+			$('#badge-ignored-' + group).text(
+				(	data[group].total == 0 ||
+					data[group].total == data[group].scanned
+					? ''
+					: (data[group].total - data[group].scanned)
+				)
+			);
 		}
-	);
+	});
 }
 
 /*  Fetch a private mail message's body (with links to attachments) where 'id'
@@ -348,247 +317,186 @@ function getMailBody(id) {
 	} else if ($('#message-' + id).html() != '') {
 		$('#message-' + id).attr('hidden', false);
 	} else {
-		$.getJSON(
-			'./api/forum.ssjs?call=get-mail-body&number=' + id,
-			function (data) {
-				var str = data.body;
-				if (data.inlines.length > 0) {
-					str +=
-						'<br>Inline attachments: ' +
-						data.inlines.join('<br>') + '<br>';
-				}
-				if (data.attachments.length > 0) {
-					str +=
-						'<br>Attachments: ' +
-						data.attachments.join('<br>') + '<br>';
-				}
-				str +=
-					'<button class="btn btn-default icon" ' +
-					'aria-label="Reply to this message" ' +
-					'title="Reply to this message" ' +
-					'name="reply-' + id + '" ' +
-					'onclick="addReply(\'mail\',' + id + ')">' +
-					'<span class="glyphicon glyphicon-comment"></span>' +
-					'</button>' +
-					'<button class="btn btn-default icon" aria-label="Delete this message" ' +
-					'title="Delete this message" onclick="deleteMessage(\'mail\',' + id + ')">' +
-					'<span class="glyphicon glyphicon-trash"></span>' +
-					'</button>';
-				$('#message-' + id).html(str);
-				$('#message-' + id).attr('hidden', false);
+		$.getJSON('./api/forum.ssjs?call=get-mail-body&number=' + id, function (data) {
+			var str = data.body;
+			if (data.inlines && data.inlines.length > 0) {
+				str += '<br>Inline attachments: ' + data.inlines.join('<br>') + '<br>';
 			}
-		);
+			if (data.attachments && data.attachments.length > 0) {
+				str += '<br>Attachments: ' + data.attachments.join('<br>') + '<br>';
+			}
+			str +=
+				'<button class="btn btn-default icon" ' +
+				'aria-label="Reply to this message" ' +
+				'title="Reply to this message" ' +
+				'name="reply-' + id + '" ' +
+				'onclick="addReply(\'mail\',' + id + ')">' +
+				'<span class="glyphicon glyphicon-comment"></span>' +
+				'</button>' +
+				'<button class="btn btn-default icon" aria-label="Delete this message" ' +
+				'title="Delete this message" onclick="deleteMessage(\'mail\',' + id + ')">' +
+				'<span class="glyphicon glyphicon-trash"></span>' +
+				'</button>';
+			$('#message-' + id).html(str);
+			$('#message-' + id).attr('hidden', false);
+		});
 	}
 }
 
 function setScanCfg(sub, cfg) {
-	var opts = [
-		'scan-cfg-off',
-		'scan-cfg-new',
-		'scan-cfg-youonly'
-	];
-	$.getJSON(
-		'./api/forum.ssjs?call=set-scan-cfg&sub=' + sub + '&cfg=' + cfg,
-		function (data) {
-			if (!data.success) return;
-			opts.forEach(
-				function (opt, index) {
-					$('#' + opt).toggleClass('btn-primary', (cfg == index));
-					$('#' + opt).toggleClass('btn-default', (cfg != index));
-				}
-			);
-		}
-	);
+	var opts = [ 'scan-cfg-off', 'scan-cfg-new', 'scan-cfg-youonly' ];
+	$.getJSON('./api/forum.ssjs?call=set-scan-cfg&sub=' + sub + '&cfg=' + cfg, function (data) {
+		if (!data.success) return;
+		opts.forEach(function (opt, index) {
+			$('#' + opt).toggleClass('btn-primary', (cfg == index));
+			$('#' + opt).toggleClass('btn-default', (cfg != index));
+		});
+	});
 }
 
 function threadNav() {
 
 	if (window.location.hash === '') {
-		$($('#forum-list-container').children('.list-group-item')[0]).addClass(
-			'current'
-		);
+		$($('#forum-list-container').children('.list-group-item')[0]).addClass('current');
 	} else if ($('#li-' + window.location.hash.substr(1)).length > 0) {
 		$('#li-' + window.location.hash.substr(1)).addClass('current');
 	}
 
-	$(window).keydown(
-		function (evt) {
-			var cid = $(
-				$('#forum-list-container').children('.current')[0]
-			).attr(
-				'id'
-			).substr(3);
-			switch (evt.keyCode) {
-				case 37:
-					// Left
-					window.location.hash = $('#pm-' + cid).attr('href');
-					break;
-				case 39:
-					// Right
-					window.location.hash = $('#nm-' + cid).attr('href');
-					break;
-				default:
-					break;
-			}
+	$(window).keydown(function (evt) {
+		var cid = $($('#forum-list-container').children('.current')[0]).attr('id').substr(3);
+		switch (evt.keyCode) {
+			case 37:
+				// Left
+				window.location.hash = $('#pm-' + cid).attr('href');
+				break;
+			case 39:
+				// Right
+				window.location.hash = $('#nm-' + cid).attr('href');
+				break;
+			default:
+				break;
 		}
-	);
+	});
 
-	$(window).on(
-		'hashchange',
-		function () {
-			$('#forum-list-container').children('.current').removeClass(
-				'current'
-			);
-			var id = window.location.hash.substr(1);
-			if ($('#li-' + id).length < 1) return;
-			$('#li-' + id).addClass('current');
-		}
-	);
+	$(window).on('hashchange', function () {
+		$('#forum-list-container').children('.current').removeClass('current');
+		var id = window.location.hash.substr(1);
+		if ($('#li-' + id).length < 1) return;
+		$('#li-' + id).addClass('current');
+	});
 
 }
 
 function vote(sub, id) {
 	id = id.split('-');
-	if (id.length !== 2 ||
-		(id[0] !== 'uv' && id[0] !== 'dv') ||
-		isNaN(parseInt(id[1]))
-	) {
+	if (id.length != 2 || (id[0] != 'uv' && id[0] != 'dv') || isNaN(parseInt(id[1]))) {
 		return;
 	}
-	$.getJSON(
-		'./api/forum.ssjs?call=vote&sub=' + sub + '&id=' + id[1] + '&up=' + (id[0] === 'uv' ? 1 : 0),
-		function (data) {
-			if (!data.success) return;
-			$('#' + id[0] + '-' + id[1]).addClass(
-				id[0] === 'uv' ? 'upvote-fg' : 'downvote-fg'
-			);
-			$('#' + id[0] + '-' + id[1]).attr('disabled', true);
-			$('#' + id[0] + '-' + id[1]).blur();
-			var count = parseInt($('#' + id[0] + '-count-' + id[1]).text()) + 1;
-			$('#' + id[0] + '-count-' + id[1]).text(count);
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=vote&sub=' + sub + '&id=' + id[1] + '&up=' + (id[0] === 'uv' ? 1 : 0), function (data) {
+		if (!data.success) return;
+		$('#' + id[0] + '-' + id[1]).addClass(id[0] === 'uv' ? 'upvote-fg' : 'downvote-fg');
+		$('#' + id[0] + '-' + id[1]).attr('disabled', true);
+		$('#' + id[0] + '-' + id[1]).blur();
+		var count = parseInt($('#' + id[0] + '-count-' + id[1]).text()) + 1;
+		$('#' + id[0] + '-count-' + id[1]).text(count);
+	});
 }
 
 function enableVoteButtonHandlers(sub) {
-	$('.btn-uv').click(function () { vote(sub, this.id); });
-	$('.btn-dv').click(function () { vote(sub, this.id); });
+	$('.btn-uv').click(function () {
+        vote(sub, this.id);
+    });
+	$('.btn-dv').click(function () {
+        vote(sub, this.id);
+    });
 }
 
 function getVotesInThread(sub, id) {
-	$.getJSON(
-		'./api/forum.ssjs?call=get-thread-votes&sub=' + sub + '&id=' + id,
-		function (data) {
-			Object.keys(data.m).forEach(
-				function (m) {
-					var uv = parseInt($('#uv-count-' + m).text());
-					var dv = parseInt($('#dv-count-' + m).text());
-					if (uv !== data.m[m].u) {
-						$('#uv-count-' + m).text(data.m[m].u);
-						$('#uv-' + m).addClass('indicator');
-					}
-					if (dv !== data.m[m].d) {
-						$('#dv-count-' + m).text(data.m[m].d);
-						$('#dv-' + m).addClass('indicator');
-					}
-					switch (data.m[m].v) {
-						case 1:
-							$('#uv-' + m).addClass('upvote-fg');
-							$('#uv-' + m).attr('disabled', true);							
-							$('#dv-' + m).attr('disabled', true);							
-							break;
-						case 2:
-							$('#dv-' + m).addClass('downvote-fg');
-							$('#uv-' + m).attr('disabled', true);							
-							$('#dv-' + m).attr('disabled', true);							
-							break;
-						default:
-							break;
-					}
-				}
-			);
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=get-thread-votes&sub=' + sub + '&id=' + id, function (data) {
+		Object.keys(data.m).forEach(function (m) {
+			var uv = parseInt($('#uv-count-' + m).text());
+			var dv = parseInt($('#dv-count-' + m).text());
+			if (uv !== data.m[m].u) {
+				$('#uv-count-' + m).text(data.m[m].u);
+				$('#uv-' + m).addClass('indicator');
+			}
+			if (dv !== data.m[m].d) {
+				$('#dv-count-' + m).text(data.m[m].d);
+				$('#dv-' + m).addClass('indicator');
+			}
+			switch (data.m[m].v) {
+				case 1:
+					$('#uv-' + m).addClass('upvote-fg');
+					$('#uv-' + m).attr('disabled', true);
+					$('#dv-' + m).attr('disabled', true);
+					break;
+				case 2:
+					$('#dv-' + m).addClass('downvote-fg');
+					$('#uv-' + m).attr('disabled', true);
+					$('#dv-' + m).attr('disabled', true);
+					break;
+				default:
+					break;
+			}
+		});
+	});
 }
 
 function getVotesInThreads(sub) {
-	$.getJSON(
-		'./api/forum.ssjs?call=get-sub-votes&sub=' + sub,
-		function (data) {
-			Object.keys(data).forEach(
-				function (t) {
-					var uv = data[t].p.u + ' / ' + data[t].t.u;
-					var dv = data[t].p.d + ' / ' + data[t].t.d;
-					if (uv !== $('#uv-count-' + t).text()) {
-						$('#uv-count-' + t).text(uv);
-					}
-					if (dv !== $('#dv-count-' + t).text()) {
-						$('#dv-count-' + t).text(dv);
-					}
-				}
-			);
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=get-sub-votes&sub=' + sub, function (data) {
+		Object.keys(data).forEach(function (t) {
+			var uv = data[t].p.u + ' / ' + data[t].t.u;
+			var dv = data[t].p.d + ' / ' + data[t].t.d;
+			if (uv !== $('#uv-count-' + t).text()) $('#uv-count-' + t).text(uv);
+			if (dv !== $('#dv-count-' + t).text()) $('#dv-count-' + t).text(dv);
+		});
+	});
 }
 
 function submitPollAnswers(sub, id) {
 	if ($('input[name="poll-' + id + '"]:checked').length < 1) return;
 	var answers = [];
-	$('input[name="poll-' + id + '"]:checked').each(
-		function () { answers.push($(this).val()); }
-	);
+	$('input[name="poll-' + id + '"]:checked').each(function () {
+        answers.push($(this).val());
+    });
 	answers = answers.join('&answer=');
-	$.getJSON(
-		'./api/forum.ssjs?call=submit-poll-answers&sub=' + sub + '&id=' + id + '&answer=' + answers,
-		function (data) {
-			$('input[name="poll-' + id + '"]').each(
-				function () {
-					$(this).attr('disabled', true);
-					if ($(this).prop('checked')) {
-						$(this).parent().parent().addClass('upvote-bg');
-					}
-				}
-			);
-			$('submit-poll-' + id).attr('disabled', true);
-		}
-	);
+	$.getJSON('./api/forum.ssjs?call=submit-poll-answers&sub=' + sub + '&id=' + id + '&answer=' + answers, function (data) {
+		$('input[name="poll-' + id + '"]').each(function () {
+			$(this).attr('disabled', true);
+			if ($(this).prop('checked')) {
+                $(this).parent().parent().addClass('upvote-bg');
+			}
+		});
+		$('submit-poll-' + id).attr('disabled', true);
+	});
 }
 
 function pollControl(id, count) {
-	$('input[name="poll-' + id + '"]').each(
-		function () {
-			$(this).change(
-				function () {
-					if ($('input[name="poll-' + id + '"]:checked').length >= count) {
-						$('input[name="poll-' + id + '"]:not(:checked)').each(
-							function () { $(this).attr('disabled', true); }
-						);
-					} else {
-						$('input[name="poll-' + id + '"]:not(:checked)').each(
-							function () { $(this).attr('disabled', false); }
-						);
-					}
-				}
-			);
-		}
-	);
+    $('input[name="poll-' + id + '"]').each(function () {
+		$(this).change(function () {
+			if ($('input[name="poll-' + id + '"]:checked').length >= count) {
+				$('input[name="poll-' + id + '"]:not(:checked)').each(function () {
+                    $(this).attr('disabled', true);
+                });
+			} else {
+				$('input[name="poll-' + id + '"]:not(:checked)').each(function () {
+                    $(this).attr('disabled', false);
+                });
+			}
+		});
+	});
 }
 
 function getPollData(sub, id) {
-	$.getJSON(
-		'./api/forum.ssjs?call=get-poll-results&sub=' + sub + '&id=' + id,
-		function (data) {
-			data.tally.forEach(
-				function (e, i) {
-					if (e > 0) $('#poll-count-' + id + '-' + i).text(e);
-				}
-			);
-			if (data.answers > 0) {
-				$('input[name="poll-' + id + '"]').each(
-					function () { $(this).attr('disabled', true); }
-				);
-				$('#submit-poll-' + id).attr('disabled', true);
-			}
+	$.getJSON('./api/forum.ssjs?call=get-poll-results&sub=' + sub + '&id=' + id, function (data) {
+		data.tally.forEach(function (e, i) {
+			if (e > 0) $('#poll-count-' + id + '-' + i).text(e);
+		});
+		if (data.answers > 0) {
+			$('input[name="poll-' + id + '"]').each(function () {
+                $(this).attr('disabled', true);
+            });
+			$('#submit-poll-' + id).attr('disabled', true);
 		}
-	);
-}
\ No newline at end of file
+	});
+}
-- 
GitLab


From 9fb0eeb30b58dd009a6e4e367e9e26343c09b9c6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 17:47:18 -0500
Subject: [PATCH 410/752] Accidentally undid DM's last change.

---
 web/lib/forum.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index c028d72a93..1da9ff0107 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -856,6 +856,7 @@ function getMessageThreads(sub, max) {
             auxattr : header.auxattr,
             number : header.number,
             from : header.from,
+            from_ext : header.from_ext,
             from_net_addr : header.from_net_addr,
             to : header.to,
             when_written_time : header.when_written_time,
-- 
GitLab


From 3c74bc727d64f97a8b8add0516d72b83fb858d4b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 17:55:39 -0500
Subject: [PATCH 411/752] Use locale.

---
 web/pages/.examples/002-files.xjs | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index 8bd7406f34..fd33f2bedd 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -3,8 +3,6 @@
 <?xjs
 	load(settings.web_lib + 'files.js');
 
-	var _fpl = getLanguage(settings.language_file || 'english.ini', 'page_files');
-
 	if (typeof http_request.query.dir !== 'undefined' &&
 		typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
 	) {
@@ -12,7 +10,7 @@
 	<ol class="breadcrumb">
 		<li>
 			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
-				<?xjs write(_fpl.title); ?>
+				<?xjs write(locale.strings.page_files.title); ?>
 			</a>
 		</li>
 		<li>
@@ -56,7 +54,7 @@
 	<ol class="breadcrumb">
 		<li>
 			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
-				<?xjs write(_fpl.title); ?>
+				<?xjs write(locale.strings.page_files.title); ?>
 			</a>
 		</li>
 		<li>
@@ -73,7 +71,7 @@
 			<p>
 				<?xjs write(dir.dir.description); ?>:
 				<?xjs write(dir.fileCount); ?>
-				<?xjs write(dir.fileCount === 1 ? _fpl.stat_suffix_file : _fpl.stat_suffix_files); ?>
+				<?xjs write(dir.fileCount === 1 ? locale.strings.page_files.stat_suffix_file : locale.strings.page_files.stat_suffix_files); ?>
 			</p>
 		</a>
 	<?xjs } ?>
@@ -87,7 +85,7 @@
 	<ol class="breadcrumb">
 		<li>
 			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
-				<?xjs write(_fpl.title); ?>
+				<?xjs write(locale.strings.page_files.title); ?>
 			</a>
 		</li>
 	</ol>
@@ -98,7 +96,7 @@
 			<p>
 				<?xjs write(library.description); ?>:
 				<?xjs write(library.dir_list.length); ?>
-				<?xjs write(library.dir_list.length === 1 ? _fpl.stat_suffix_directory : _fpl.stat_suffix_directories); ?>
+				<?xjs write(library.dir_list.length === 1 ? locale.strings.page_files.stat_suffix_directory : locale.strings.page_files.stat_suffix_directories); ?>
 			</p>
 		</a>
 	<?xjs } ?>
-- 
GitLab


From a9cb9ce0ae9aac433e0acef5aa80d1e3d5b034ae Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 17:59:43 -0500
Subject: [PATCH 412/752] Use locale.

---
 web/root/api/system.ssjs | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 4625157958..a885cc897c 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -7,8 +7,6 @@ load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'language.js');
 
-var _language = getLanguage(settings.language_file || 'english.ini', 'api_system');
-
 var reply = {};
 
 if ((http_request.method === 'GET' || http_request.method === 'POST') &&
@@ -19,18 +17,18 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 	switch (http_request.query.call[0]) {
 
 		case 'node-list':
-      var usr = new User(1);
+            var usr = new User(1);
 			reply = system.node_list.reduce(function (a, c, i) {
-        if (c.status !== 3) return a;
-        usr.number = c.useron;
+                if (c.status !== 3) return a;
+                usr.number = c.useron;
 				a.push({
-          node : i,
-          status : format(NodeStatus[c.status], c.aux, c.extaux),
-					action : format(NodeAction[c.action], c.aux, c.extaux),
-          user : usr.alias,
-          connection : usr.connection
+                    node: i,
+                    status: format(NodeStatus[c.status], c.aux, c.extaux),
+					action: format(NodeAction[c.action], c.aux, c.extaux),
+                    user: usr.alias,
+                    connection: usr.connection
 				});
-        return a;
+                return a;
 			}, []);
 			for (var un = 1; un < system.lastuser; un++) {
 				usr.number = un;
@@ -41,13 +39,13 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				var webAction = getSessionValue(usr.number, 'action');
 				if (webAction === null) continue;
 				reply.push({
-          status : '',
-					action : _language.nodelist_action_prefix + ' ' + webAction,
-					user : usr.alias,
-          connection : 'W'
+                    status: '',
+					action: locale.strings.api_system.nodelist_action_prefix + ' ' + webAction,
+					user: usr.alias,
+                    connection: 'W'
 				});
 			}
-      usr = undefined;
+            usr = undefined;
 			break;
 
 		case 'send-telegram':
@@ -67,7 +65,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			if (un < 1) break;
 			system.put_telegram(
 				un, format(
-					_language.telegram_header_format,
+					locale.strings.api_system.telegram_header_format,
 					user.alias, (new Date()).toLocaleString()
 				) + '\r\n' + http_request.query.telegram[0] + '\r\n'
 			);
-- 
GitLab


From b1a25585a26dd0252315c718c672816f03acee8d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 18:00:21 -0500
Subject: [PATCH 413/752] Use locale.

---
 web/root/api/system.ssjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index a885cc897c..527d73854b 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -5,7 +5,6 @@ var settings = get_mod_options('web');
 
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
-load(settings.web_lib + 'language.js');
 
 var reply = {};
 
-- 
GitLab


From 9014d6959c3d7c0982e2ea03b808fe39f4d8d139 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 18:02:06 -0500
Subject: [PATCH 414/752] Use locale.

---
 web/root/api/register.ssjs | 119 +++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 59 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 0561c6e3c3..58178b73e8 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,16 +1,10 @@
 load('sbbsdefs.js');
-load('modopts.js');
-var settings = get_mod_options('web');
-
-load(settings.web_directory + '/lib/init.js');
+load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + '/auth.js');
-load(settings.web_lib + '/language.js');
 
 if (user.alias !== settings.guest) exit();
 if (!settings.user_registration) exit();
 
-var _rl = getLanguage(settings.language_file || 'english.ini', 'api_register');
-
 var MIN_ALIAS = 1,
 	MIN_REALNAME = 3,
 	MIN_NETMAIL = 6,
@@ -18,7 +12,6 @@ var MIN_ALIAS = 1,
 	MIN_ADDRESS = 6,
 	MIN_PHONE = 3;
 
-
 var reply = {
 	errors : [],
 	userNumber : 0
@@ -45,9 +38,8 @@ function required(mask) {
 function cleanParam(param) {
 	if (paramExists(param)) {
 		return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
-	} else {
-	   return "";
-    }
+	}
+	return "";
 }
 
 function paramExists(param) {
@@ -55,9 +47,8 @@ function paramExists(param) {
 		http_request.query[param][0] !== ''
 	) {
 		return true;
-	} else {
-	    return false;
-    }
+	}
+	return false;
 }
 
 function paramLength(param) {
@@ -75,10 +66,10 @@ function paramLength(param) {
 function newUser() {
 	var usr = system.new_user(prepUser.alias);
 	if (typeof usr === 'number') {
-		reply.errors.push(_rl.error_failed);
+		reply.errors.push(locale.strings.api_register.error_failed);
 		return;
 	}
-	log(LOG_INFO, format(_rl.log_success, usr.number));
+	log(LOG_INFO, format(locale.strings.api_register.log_success, usr.number));
 	usr.security.password = prepUser.password;
 	for (var property in prepUser) {
 		if (property === 'alias' || property === 'password') continue;
@@ -95,7 +86,7 @@ if ((	paramExists('send-me-free-stuff') &&
 		http_request.query['subscribe-to-newsletter'][0] !== ''
 	)
 ) {
-	log(LOG_WARNING, _rl.log_bot_attempt);
+	log(LOG_WARNING, locale.strings.api_register.log_bot_attempt);
 	exit();
 }
 
@@ -104,7 +95,7 @@ if (system.newuser_password !== '' &&
 		http_request.query['newuser-password'][0] != system.newuser_password
 	)
 ) {
-	reply.errors.push(_rl.error_bad_syspass);
+	reply.errors.push(locale.strings.api_register.error_bad_syspass);
 }
 
 // More could be done to respect certain newuser question toggles
@@ -114,9 +105,9 @@ if (!paramExists('alias') ||
 	paramLength('alias') < MIN_ALIAS ||
 	paramLength('alias') > LEN_ALIAS
 ) {
-	reply.errors.push(_rl.error_invalid_alias);
+	reply.errors.push(locale.strings.api_register.error_invalid_alias);
 } else if (system.matchuser(http_request.query.alias[0]) > 0) {
-	reply.errors.push(_rl.error_alias_taken);
+	reply.errors.push(locale.strings.api_register.error_alias_taken);
 } else {
 	prepUser.alias = cleanParam('alias');
 	prepUser.handle = cleanParam('alias');
@@ -125,14 +116,14 @@ if (!paramExists('alias') ||
 if ((!paramExists('password1') || !paramExists('password2')) ||
 	http_request.query.password1[0] !== http_request.query.password2[0]
 ) {
-	reply.errors.push(_rl.error_password_mismatch);
+	reply.errors.push(locale.strings.api_register.error_password_mismatch);
 } else if (
 	paramLength('password1') < settings.minimum_password_length ||
 	paramLength('password1') > LEN_PASS
 ) {
 	reply.errors.push(
 		format(
-			_rl.error_password_length,
+			locale.strings.api_register.error_password_length,
 			settings.minimum_password_length, LEN_PASS
 		)
 	);
@@ -141,64 +132,74 @@ if ((!paramExists('password1') || !paramExists('password2')) ||
 }
 
 if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
-	reply.errors.push(_rl.error_email_required);
+	reply.errors.push(locale.strings.api_register.error_email_required);
 } else if (
 	(	paramLength('netmail') < MIN_NETMAIL ||
 		paramLength('netmail') > LEN_NETMAIL
 	) && !required(UQ_NONETMAIL)
 ) {
-	reply.errors.push(_rl.error_invalid_email);
+	reply.errors.push(locale.strings.api_register.error_invalid_email);
 } else {
 	prepUser.netmail = cleanParam('netmail');
 }
 
-if (paramExists('realname') &&
-    paramLength('realname') >= MIN_REALNAME &&
-    paramLength('realname') <= LEN_NAME
+if (required(UQ_REALNAME) &&
+	(	!paramExists('realname') ||
+		paramLength('realname') < MIN_REALNAME ||
+		paramLength('realname') > LEN_NAME
+	)
 ) {
-    prepUser.name = cleanParam('realname');
-} else if (required(UQ_REALNAME)) {
-    reply.errors.push(_rl.error_invalid_name);
+	reply.errors.push(locale.strings.api_register.error_invalid_name);
+} else {
+	prepUser.name = cleanParam('realname');
 }
 
-if (paramExists('location') &&
-    paramLength('location') >= MIN_LOCATION &&
-    paramLength('location') <= LEN_LOCATION
+if (required(UQ_LOCATION) &&
+	(	!paramExists('location') ||
+		paramLength('location') < MIN_LOCATION ||
+		paramLength('location') > LEN_LOCATION
+	)
 ) {
-    prepUser.location = cleanParam('location');
-} else if (required(UQ_LOCATION)) {
-    reply.errors.push(_rl.error_invalid_location);
+	reply.errors.push(locale.strings.api_register.error_invalid_location);
+} else {
+	prepUser.location = cleanParam('location');
 }
 
-if (paramExists('address') &&
-    paramLength('address') >= MIN_ADDRESS &&
-    paramLength('address') <= LEN_ADDRESS &&
-    paramExists('zipcode') &&
-    paramLength('zipcode') >= 3 &&
-    paramLength('zipcode') <= LEN_ADDRESS
+if (required(UQ_ADDRESS) &&
+	(	!paramExists('address') ||
+		paramLength('address') < MIN_ADDRESS ||
+		paramLength('address') > LEN_ADDRESS ||
+		!paramExists('zipcode') ||
+		paramLength('zipcode') < 3 ||
+		paramLength('zipcode') > LEN_ADDRESS
+	)
 ) {
-    prepUser.address = cleanParam('address');
-    prepUser.zipcode = cleanParam('zipcode');
-} else if (required(UQ_ADDRESS)) {
-    reply.errors.push(_rl.error_invalid_street_address);
+	reply.errors.push(locale.strings.api_register.error_invalid_street_address);
+} else {
+	prepUser.address = cleanParam('address');
+	prepUser.zipcode = cleanParam('zipcode');
 }
 
-if (paramExists('phone') &&
-    paramLength('phone') >= MIN_PHONE &&
-    paramLength('phone') <= LEN_PHONE
+if (required(UQ_PHONE) &&
+	(	!paramExists('phone') ||
+		paramLength('phone') < MIN_PHONE ||
+		paramLength('phone') > LEN_PHONE
+	)
 ) {
-    prepUser.phone = cleanParam('phone');
-} else if (required(UQ_PHONE)) {
-    reply.errors.push(_rl.error_invalid_phone);
+	reply.errors.push(locale.strings.api_register.error_invalid_phone);
+} else {
+	prepUser.phone = cleanParam('phone');
 }
 
-if (paramExists('gender') &&
-    paramLength('gender') == 1 &&
-    ['X', 'M', 'F', 'O'].indexOf(http_request.query.gender[0] > -1)
+if (required(UQ_SEX) &&
+	(	!paramExists('gender') ||
+		paramLength('gender') != 1 ||
+		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
+	)
 ) {
-    prepUser.gender = http_request.query.gender[0];
-} else if (required(UQ_SEX)) {
-    reply.errors.push(_rl.error_invalid_gender);
+	reply.errors.push(locale.strings.api_register.error_invalid_gender);
+} else {
+	prepUser.gender = http_request.query.gender[0];
 }
 
 if (paramExists('birth') &&
@@ -207,7 +208,7 @@ if (paramExists('birth') &&
 	// Should really test for valid date (and date format per system config)
 	prepUser.birthdate = cleanParam('birth');
 } else if (required(UQ_BIRTH)) {
-	reply.errors.push(_rl.error_invalid_birthdate);
+	reply.errors.push(locale.strings.api_register.error_invalid_birthdate);
 }
 
 if (reply.errors.length < 1) newUser();
-- 
GitLab


From c6e6f3a938056d8372a43244fcc221d89c962f01 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 18:04:11 -0500
Subject: [PATCH 415/752] Use locale.

---
 web/root/index.xjs | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index cae608b34e..27fb640498 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -10,8 +10,6 @@
 	load(settings.web_lib + 'sidebar.js');
 	load(settings.web_lib + 'language.js');
 
-	var _language = getLanguage(settings.language_file || 'english.ini', 'main');
-
 	var page = (
 		(	typeof http_request.query.page === 'undefined' ||
 			!file_exists(
@@ -117,10 +115,10 @@
 					<div class="modal-body" id="popUpModalBody"></div>
 					<div class="modal-footer">
 						<button type="button" class="btn btn-default" id="popUpModalCloseButton">
-							<?xjs write(_language.button_close); ?>
+							<?xjs write(locale.strings.main.button_close); ?>
 						</button>
 						<button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>
-							<?xjs write(_language.button_submit); ?>
+							<?xjs write(locale.strings.main.button_submit); ?>
 						</button>
 					</div>
 				</div>
@@ -132,7 +130,7 @@
 				<div class="navbar-header">
 					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
 						<span class="sr-only">
-							<?xjs write(_language.label_sidebar); ?>
+							<?xjs write(locale.strings.main.label_sidebar); ?>
 						</span>
 						<span class="icon-bar"></span>
 						<span class="icon-bar"></span>
@@ -151,26 +149,26 @@
 							<?xjs if (settings.user_registration) { ?>
 								<li>
 									<a href="./?page=000-register.xjs">
-										<?xjs write(_language.menu_item_register); ?>
+										<?xjs write(locale.strings.main.menu_item_register); ?>
 									</a>
 								</li>
 							<?xjs } ?>
 							<li class="dropdown">
 								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-									<?xjs write(_language.menu_item_login); ?>
+									<?xjs write(locale.strings.main.menu_item_login); ?>
 									<span class="caret"></span>
 								</a>
 								<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
 									<form id="form-login">
 										<label for="input-username" class="sr-only">
-											<?xjs write(_language.input_username); ?>
+											<?xjs write(locale.strings.main.input_username); ?>
 										</label>
-										<input id="input-username" title="<?xjs write(_language.input_username); ?>" type="text" class="dropdown form-control" placeholder="<?xjs write(_language.input_username); ?>">
+										<input id="input-username" title="<?xjs write(locale.strings.main.input_username); ?>" type="text" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_username); ?>">
 										<label for="input-password" class="sr-only">
-											<?xjs write(_language.input_password); ?>
+											<?xjs write(locale.strings.main.input_password); ?>
 										</label>
-										<input id="input-password" title="<?xjs write(_language.input_password); ?>" type="password" class="dropdown form-control" placeholder="<?xjs write(_language.input_password); ?>">
-										<input id="button-login" class="dropdown btn btn-primary" type="submit" value="<?xjs write(_language.button_login); ?>">
+										<input id="input-password" title="<?xjs write(locale.strings.main.input_password); ?>" type="password" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_password); ?>">
+										<input id="button-login" class="dropdown btn btn-primary" type="submit" value="<?xjs write(locale.strings.main.button_login); ?>">
 									</form>
 								</div>
 							</li>
@@ -184,13 +182,13 @@
 								<ul class="dropdown-menu">
 									<li>
 										<a href="./?page=000-mail.xjs">
-											<?xjs write(_language.menu_item_mail); ?>
-											<span class="badge scanned" title="<?xjs write(_language.label_unread_mail); ?>" id="badge-unread-mail-inner"></span>
+											<?xjs write(locale.strings.main.menu_item_mail); ?>
+											<span class="badge scanned" title="<?xjs write(locale.strings.main.label_unread_mail); ?>" id="badge-unread-mail-inner"></span>
 										</a>
 									</li>
 									<li>
 										<a id="button-logout" href="#">
-											<?xjs write(_language.menu_item_logout); ?>
+											<?xjs write(locale.strings.main.menu_item_logout); ?>
 										</a>
 									</li>
 								</ul>
@@ -207,7 +205,7 @@
 				<div class="col-xs-12 col-sm-<?xjs write(settings.layout_sidebar_off || page_ctrl.options.no_sidebar ? 12 : 9); ?>">
 					<div style="clear:both;">
 					<p class="pull-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?> visible-xs">
-						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><span class="glyphicon glyphicon-tasks"></span><?xjs write(_language.label_sidebar); ?></button>
+						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><span class="glyphicon glyphicon-tasks"></span><?xjs write(locale.strings.main.label_sidebar); ?></button>
 					</p>
 					</div>
 					<?xjs writePage(); ?>
-- 
GitLab


From a0a56af0cb4de997dd9e4b017318cc3b375e0c8f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 18:04:44 -0500
Subject: [PATCH 416/752] Don't load language.js

---
 web/root/index.xjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 27fb640498..ee5d51c8d4 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -8,7 +8,6 @@
 	load(settings.web_lib + 'auth.js');
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
-	load(settings.web_lib + 'language.js');
 
 	var page = (
 		(	typeof http_request.query.page === 'undefined' ||
-- 
GitLab


From 33cba502987c1321817b72a92b2ca149bfc9316f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 18:05:26 -0500
Subject: [PATCH 417/752] No longer needed.

---
 web/lib/language.js | 17 -----------------
 1 file changed, 17 deletions(-)
 delete mode 100644 web/lib/language.js

diff --git a/web/lib/language.js b/web/lib/language.js
deleted file mode 100644
index 9e0de939d7..0000000000
--- a/web/lib/language.js
+++ /dev/null
@@ -1,17 +0,0 @@
-function getLanguage(file, section) {
-
-	var stock = 'english.ini';
-
-	if (!file_exists(settings.web_lib + 'language/' + file)) {
-		return getLanguage(stock, section);
-	}
-
-	var f = new File(settings.web_lib + 'language/' + file);
-	if (!f.open('r')) throw 'Unable to open language file ' + file;
-	var ini = f.iniGetObject(section);
-	f.close();
-	if (ini === null && file !== stock) ini = getLanguage(stock, section);
-
-	return ini;
-
-}
\ No newline at end of file
-- 
GitLab


From 957c22c91af944d6ba165dd21d167d7b8dfa464a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 14 Jan 2019 18:06:53 -0500
Subject: [PATCH 418/752] No longer needed.

---
 web/lib/language/english.ini | 130 -----------------------------------
 1 file changed, 130 deletions(-)
 delete mode 100644 web/lib/language/english.ini

diff --git a/web/lib/language/english.ini b/web/lib/language/english.ini
deleted file mode 100644
index 0a9ab4fd19..0000000000
--- a/web/lib/language/english.ini
+++ /dev/null
@@ -1,130 +0,0 @@
-[main]
-button_close = Close
-button_submit = Submit
-button_login = Log in
-input_username = Username
-input_password = Password
-label_sidebar = Sidebar
-label_unread_mail = Unread mail
-menu_item_login = Log in
-menu_item_logout = Log out
-menu_item_mail = Mail
-menu_item_register = Register
-
-[page_home]
-button_ftelnet = Connect via Telnet
-
-[page_mail]
-button_post_new = Post a new message
-button_select_all = Select all messages
-button_delete_selected = Delete selected messages
-label_message_from = From
-label_message_to = To
-label_message_date = on
-label_message_subject = Subject
-label_tab_inbox = Inbox
-label_tab_sent = Sent
-
-[page_register]
-title = Register
-button_register = Register
-label_field_required = Required
-input_alias = Username
-input_password = Password
-input_password_confirm = Confirm password
-input_email = Email address
-input_name = Real name
-input_street_address = Street address
-input_zipcode = Postal code
-input_location = Location (City, State/Province)
-input_phone = Telephone number
-input_birthdate = Birthdate
-input_gender = Gender
-input_gender_withheld = Withheld
-input_gender_male = Male
-input_gender_female = Female
-input_gender_other = Other
-input_registration_password = Registration password
-stat_suffix_field_required = required
-help_text_required = Fields marked with an asterisk are required.  All others can be left blank if you wish.
-help_text_minimum_characters = Minimum of %d characters
-help_text_maximum_characters = Maximum of %d characters
-message_account_created = Your account has been created
-placeholder_netmail = pat@m.f
-placeholder_name = Pat Androgyne
-placeholder_street_address = 123 Any Street
-placeholder_zipcode = H0H0H0
-placeholder_location = City, State
-placeholder_phone = 800-555-5555
-
-[page_files]
-title = Files
-stat_suffix_file = file
-stat_suffix_files = files
-stat_suffix_directory = directory
-stat_suffix_directories = directories
-
-[page_forum]
-title = XJS Test Forum
-label_thread_from = By
-label_message_from = From
-label_message_to = To
-label_message_date = on
-label_message_subject = Subject
-label_thread_latest_reply = Latest reply by
-badge_poll = POLL
-badge_downvotes = Downvotes - Parent / Thread Total
-badge_upvotes = Upvotes - Parent / Thread Total
-button_post_message = Post a new message
-button_post_poll = Post a new poll
-button_scan_new = Include in new message scan
-button_scan_you = Include in new message scan (messages to you only)
-button_scan_off = Exclude from new message scan
-button_load_more = Load more
-message_loading_threads = Loading threads ...
-
-[sidebar_node_list]
-label_title = Who's Online
-label_connection_column = Via
-label_node_column = Node
-label_send_telegram = Send a telegram
-label_status_column = Status
-
-[sidebar_system_info]
-label_title = System Info
-label_sysop = Sysop:
-label_location = Location:
-label_users = Users:
-label_nodes = Nodes:
-label_uptime = Uptime:
-label_calls_total = Calls:
-label_calls_today = Calls today:
-label_files_total = Files:
-label_files_uploaded_today = U/L today:
-label_files_downloaded_today = D/L today:
-label_messages_total = Messages:
-label_messages_posted_today = Posted today:
-stat_suffix_files = files
-stat_suffix_bytes = bytes
-
-[api_system]
-nodelist_action_prefix = viewing
-telegram_header_format = Telegram from %s via WWW on %s
-
-[api_register]
-log_success = User #%d registered via HTTP
-log_bot_attempt = Hidden registration form field filled. Likely a bot. Cancelling registration.
-error_failed = Failed to create user record.
-error_bad_syspass = Incorrect registration password.
-error_invalid_alias = Valid username is required.
-error_alias_taken = Username already taken.
-error_password_mismatch = Password and confirmation are required, and must match.
-error_password_length = Password must be %d to %d characters in length.
-error_email_required = Valid email address is required.
-error_invalid_email = Invalid email address
-error_invalid_name = Valid real name is required.
-error_invalid_location = Valid location is required.
-error_invalid_street_address = Valid street address and postal code are required
-error_invalid_phone = Valid phone number is required.
-error_invalid_gender = Sex is required. Heh heh heh.
-error_invalid_birthdate = Valid birthdate is required.
-- 
GitLab


From d05780e2977c3af858cf2aaf8ed0fe366b9cb83b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Jan 2019 18:10:54 -0500
Subject: [PATCH 419/752] Cheesy weather widget. Requires an API key from
 openweathermap.org. Add to ctrl/modopts.ini: [openweathermap] api_key = [your
 api key]

---
 web/sidebar/.extras/openweathermap.ssjs | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 web/sidebar/.extras/openweathermap.ssjs

diff --git a/web/sidebar/.extras/openweathermap.ssjs b/web/sidebar/.extras/openweathermap.ssjs
new file mode 100644
index 0000000000..c269e60c6a
--- /dev/null
+++ b/web/sidebar/.extras/openweathermap.ssjs
@@ -0,0 +1,23 @@
+// Requires an API key from https://openweathermap.org/
+// Add an [openweathermap] section to ctrl/modopts.ini
+// Add an api_key value to that section
+(function () {
+    try {
+        load('geoip.js');
+        require('openweathermap.js', 'OpenWeatherMap');
+        const geoip = get_geoip(
+            http_request.header['x-forwarded-for'] || http_request.remote_ip
+        );
+        const owm = new OpenWeatherMap();
+        const wq = { units: 'metric', mode: 'html' };
+        if (geoip.latitude && geoip.longitude) {
+            wq.lat = geoip.latitude;
+            wq.lon = geoip.longitude;
+        } else if (geoip.cityName) {
+            wq.q = geoip.cityName;
+        }
+        writeln(owm.call_api('weather', wq, true).data);
+    } catch (err) {
+        // meh
+    }
+})();
-- 
GitLab


From 750449cc7d9d2abd20949fba0187745e3d0fd74d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 21 Jan 2019 18:15:04 -0500
Subject: [PATCH 420/752] Pass correct parameters to get_msg_body (should fix
 plaintext thing).

---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 1da9ff0107..c65523a41a 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -308,8 +308,8 @@ function getMailBody(number) {
     if (!msgBase.open()) return ret;
     var header = msgBase.get_msg_header(false, number, false);
     if (header !== null && (header.to_ext == user.number || header.from_ext == user.number)) {
-        const body = msgBase.get_msg_body(false, number, header);
-        const pt_body = msgBase.get_msg_body(false, number, header, false, false, true, true); // Plaintext body - this doesn't work.
+        const body = msgBase.get_msg_body(false, header);
+        const pt_body = msgBase.get_msg_body(false, header, false, false, true, true);
         if (header.to_ext == user.number && (header.attr^MSG_READ)) {
             header.attr|=MSG_READ;
             msgBase.put_msg_header(false, number, header);
-- 
GitLab


From 6f406ff894969f6841650dbedd5d63797ca0e435 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 26 Jan 2019 01:56:23 -0800
Subject: [PATCH 421/752] Beautify the services listing and sort by
 verification results.

If a service has a description, display it (in parens).
If a service uses a standard TCP port, don't display the port.
Use the special "verify" sort order. I'm not sure why the previous
sorted list was reversed, but in this change, the most frequently
and recently verified BBSes will be at the top.
---
 web/pages/.examples/More/999-sbbslist.xjs | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/web/pages/.examples/More/999-sbbslist.xjs b/web/pages/.examples/More/999-sbbslist.xjs
index 0ef2847566..fc67babcff 100644
--- a/web/pages/.examples/More/999-sbbslist.xjs
+++ b/web/pages/.examples/More/999-sbbslist.xjs
@@ -36,6 +36,7 @@
 <?xjs
 
 	load('graphic.js');
+	require('portdefs.js', 'standard_service_port');
 	var lib = load({}, "sbbslist_lib.js");
 
 	function bbs_preview(bbs) {
@@ -61,8 +62,7 @@
 
 	var list = lib.read_list();
 	if (list === null) list = [];
-	lib.sort(list, "verified_on");
-	list.reverse();
+	lib.sort(list, "verify");
 
 	list.forEach(
 		function (e) {
@@ -73,8 +73,12 @@
 					function (ee) {
                         if (!ee.protocol)
                             return '';
-						var r = ee.protocol[0].toUpperCase() + ee.protocol.slice(1) + ': ' + ee.address;
-						if (typeof ee.port === 'number') r += ':' + ee.port;
+						var r = ee.protocol + ': ' + ee.address;
+						if (typeof ee.port === 'number'
+                            && ee.port != standard_service_port[ee.protocol])
+                            r += ':' + ee.port;
+                        if (ee.description !== undefined && ee.description.length > 0)
+                            r += ' (' + ee.description + ')';
 						return r;
 					}
 				).join ('<br>'),
-- 
GitLab


From 1c2359c5f6917ed6a6cfd255ee39c50cd41b726d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 26 Jan 2019 10:32:05 -0500
Subject: [PATCH 422/752] get_mail_headers function with inbox/spam/sent
 filtering.

---
 web/lib/forum.js | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index c65523a41a..404cd79177 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -267,6 +267,30 @@ function getMailHeaders(sent, ascending) {
     return headers;
 }
 
+function get_mail_headers(filter, ascending) {
+    if (filter == 'sent') {
+        if (user.security.restrictions&UFLAG_K) return []; // I don't remember what this is for.
+    }
+    const headers = [];
+    const msg_base = new MsgBase('mail');
+    if (!msg_base.open()) return headers;
+    for (var n = msg_base.first_msg; n <= msg_base.last_msg; n++) {
+        var h = msg_base.get_msg_header(n);
+        if (h === null || h.attr&MSG_DELETE) continue;
+        if (filter == 'sent') {
+            if (h.from_ext == user.number) headers.push(h);
+        } else if (h.to_ext == user.number) {
+            if (h.subject.search(/^SPAM:/) > -1) {
+                if (filter == 'spam') headers.push(h);
+            } else if (filter != 'spam') {
+                headers.push(h);
+            }
+        }
+    }
+    msg_base.close();
+    return ascending ? headers.reverse() : headers;
+}
+
 function mimeDecode(header, body, code) {
     const ret = {
         type : '',
-- 
GitLab


From fd33278dea4f5e616a1cea8dee9cd1cca0f1253c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 26 Jan 2019 10:32:26 -0500
Subject: [PATCH 423/752] Added a spam tab.

---
 web/pages/.examples/000-mail.xjs | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index 77f6d96f76..f168c1d129 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -6,10 +6,10 @@
     load('sbbsdefs.js');
     load(settings.web_lib + 'forum.js');
 
-    function sent() {
-    	if (typeof http_request.query.sent === 'undefined') return false;
-    	if (http_request.query.sent[0] == '0') return false;
-    	return true;
+    const _mail_tab = http_request.query.tab ? http_request.query.tab[0] : 'inbox';
+
+    function _active_tab(tab) {
+        return tab == _mail_tab ? 'active' : '';
     }
 ?>
 
@@ -24,8 +24,8 @@
 				</div>
 			</div>
 			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
-				<?xjs write(!sent() ? locale.strings.page_mail.label_message_from : locale.strings.page_mail.label_message_to); ?>:
-				<strong><?xjs write(!sent() ? header.from : header.to); ?></strong>
+				<?xjs write(_mail_tab == 'sent' ? locale.strings.page_mail.label_message_from : locale.strings.page_mail.label_message_to); ?>:
+				<strong><?xjs write(_mail_tab == 'sent' ? header.from : header.to); ?></strong>
 				<?xjs write(locale.strings.page_mail.label_message_date); ?>
 				<?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
 				<p>
@@ -87,13 +87,18 @@
 <?xjs } ?>
 
 <ul class="nav nav-tabs">
-	<li role="presentation" class="<?xjs write(!sent() ? ' active' : ''); ?>">
-		<a href="./?page=<?xjs write(page); ?>&amp;sent=0">
+	<li role="presentation" class="<?xjs write(_active_tab('inbox')); ?>">
+		<a href="./?page=<?xjs write(page); ?>&amp;tab=inbox">
 			<?xjs write(locale.strings.page_mail.label_tab_inbox); ?>
 		</a>
 	</li>
-	<li role="presentation" class="<?xjs write(sent() ? ' active' : ''); ?>">
-		<a href="./?page=<?xjs write(page); ?>&amp;sent=1">
+    <li role="presentation" class="<?xjs write(_active_tab('spam')); ?>">
+		<a href="./?page=<?xjs write(page); ?>&amp;tab=spam">
+			Spam
+		</a>
+	</li>
+	<li role="presentation" class="<?xjs write(_active_tab('sent')); ?>">
+		<a href="./?page=<?xjs write(page); ?>&amp;tab=sent">
 			<?xjs write(locale.strings.page_mail.label_tab_sent); ?>
 		</a>
 	</li>
@@ -101,5 +106,5 @@
 <br>
 
 <ul id="forum-list-container" class="list-group">
-	<?xjs getMailHeaders(sent()).forEach(writeMessage); ?>
+    <?xjs get_mail_headers(_mail_tab).forEach(writeMessage); ?>
 </ul>
-- 
GitLab


From 3805982fdcdf574284bdfd4f4ce73cd12dad960e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 26 Jan 2019 11:01:07 -0500
Subject: [PATCH 424/752] Added read/unread indicators to the tabs.

---
 web/lib/forum.js                 | 29 ++++++++++++++++++-----------
 web/pages/.examples/000-mail.xjs | 22 ++++++++++++++++++----
 2 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 404cd79177..a67a2d146c 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -268,27 +268,34 @@ function getMailHeaders(sent, ascending) {
 }
 
 function get_mail_headers(filter, ascending) {
-    if (filter == 'sent') {
-        if (user.security.restrictions&UFLAG_K) return []; // I don't remember what this is for.
-    }
-    const headers = [];
+    const ret = {
+        headers: [],
+        sent: { read: 0, unread: 0 },
+        spam: { read: 0, unread: 0 },
+        inbox: { read: 0, unread: 0 }
+    };
+    if (filter == 'sent' && user.security.restrictions&UFLAG_K) return ret; // I don't remember what this is for.
     const msg_base = new MsgBase('mail');
-    if (!msg_base.open()) return headers;
+    if (!msg_base.open()) return ret;
     for (var n = msg_base.first_msg; n <= msg_base.last_msg; n++) {
         var h = msg_base.get_msg_header(n);
         if (h === null || h.attr&MSG_DELETE) continue;
-        if (filter == 'sent') {
-            if (h.from_ext == user.number) headers.push(h);
+        if (h.from_ext == user.number) {
+            h.attr&MSG_READ ? ret.sent.read++ : (ret.sent.unread++);
+            if (filter == 'sent') ret.headers.push(h);
         } else if (h.to_ext == user.number) {
             if (h.subject.search(/^SPAM:/) > -1) {
-                if (filter == 'spam') headers.push(h);
-            } else if (filter != 'spam') {
-                headers.push(h);
+                h.attr&MSG_READ ? ret.spam.read++ : (ret.spam.unread++);
+                if (filter == 'spam') ret.headers.push(h);
+            } else {
+                h.attr&MSG_READ ? ret.inbox.read++ : (ret.inbox.unread++);
+                if (filter != 'spam') ret.headers.push(h);
             }
         }
     }
     msg_base.close();
-    return ascending ? headers.reverse() : headers;
+    if (ascending) ret.headers.reverse();
+    return ret;
 }
 
 function mimeDecode(header, body, code) {
diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index f168c1d129..6c174c348a 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -2,12 +2,10 @@
 
 <?xjs
     if (user.number == 0 || user.alias == settings.guest) exit();
-
     load('sbbsdefs.js');
     load(settings.web_lib + 'forum.js');
-
     const _mail_tab = http_request.query.tab ? http_request.query.tab[0] : 'inbox';
-
+    const _mail = get_mail_headers(_mail_tab, true);
     function _active_tab(tab) {
         return tab == _mail_tab ? 'active' : '';
     }
@@ -38,6 +36,19 @@
 	</li>
 <?xjs } ?>
 
+<?xjs
+    function _read_unread(tab) {
+        write('(');
+        if (_mail[tab].unread) {
+            write('<strong>' + _mail[tab].unread + '</strong>');
+        } else {
+            write(_mail[tab].unread);
+        }
+        write('/' + _mail[tab].read);
+        write(')');
+    }
+?>
+
 <script type="text/javascript" src="./js/forum.js"></script>
 
 <script type="text/javascript">
@@ -90,21 +101,24 @@
 	<li role="presentation" class="<?xjs write(_active_tab('inbox')); ?>">
 		<a href="./?page=<?xjs write(page); ?>&amp;tab=inbox">
 			<?xjs write(locale.strings.page_mail.label_tab_inbox); ?>
+            <?xjs _read_unread('inbox'); ?>
 		</a>
 	</li>
     <li role="presentation" class="<?xjs write(_active_tab('spam')); ?>">
 		<a href="./?page=<?xjs write(page); ?>&amp;tab=spam">
 			Spam
+            <?xjs _read_unread('spam'); ?>
 		</a>
 	</li>
 	<li role="presentation" class="<?xjs write(_active_tab('sent')); ?>">
 		<a href="./?page=<?xjs write(page); ?>&amp;tab=sent">
 			<?xjs write(locale.strings.page_mail.label_tab_sent); ?>
+            <?xjs _read_unread('sent'); ?>
 		</a>
 	</li>
 </ul>
 <br>
 
 <ul id="forum-list-container" class="list-group">
-    <?xjs get_mail_headers(_mail_tab).forEach(writeMessage); ?>
+    <?xjs _mail.headers.forEach(writeMessage); ?>
 </ul>
-- 
GitLab


From bdee562f30ec2962316dade1c6505b9542632682 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 27 Jan 2019 01:46:23 -0800
Subject: [PATCH 425/752] Check the "SPAM" msg attribute flag as well as the
 message subject.

When categorizing SPAM messages, not all spam message may have a
subject beginning with the word "SPAM:" (depending on configuration
and how the message was flagged as SPAM), so check the attribute
flag as well. The subject check is probably redundant at this point.
---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index a67a2d146c..2cf169c389 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -284,7 +284,7 @@ function get_mail_headers(filter, ascending) {
             h.attr&MSG_READ ? ret.sent.read++ : (ret.sent.unread++);
             if (filter == 'sent') ret.headers.push(h);
         } else if (h.to_ext == user.number) {
-            if (h.subject.search(/^SPAM:/) > -1) {
+            if ((h.attr&MSG_SPAM) || (h.subject.search(/^SPAM:/) > -1)) {
                 h.attr&MSG_READ ? ret.spam.read++ : (ret.spam.unread++);
                 if (filter == 'spam') ret.headers.push(h);
             } else {
-- 
GitLab


From 7ba70795f7b96fe974cd0e3301bc47af03652eb8 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 27 Jan 2019 02:32:45 -0800
Subject: [PATCH 426/752] Reverse the to/from display choice for the mail
 folders

We want to display the *from* field for messages in the Inbox and Spam
tabs and we want to display the *to* field for messages in the Sent
tab. I'm prettty sure. :-)
---
 web/pages/.examples/000-mail.xjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index 6c174c348a..505b6841dd 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -22,8 +22,8 @@
 				</div>
 			</div>
 			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
-				<?xjs write(_mail_tab == 'sent' ? locale.strings.page_mail.label_message_from : locale.strings.page_mail.label_message_to); ?>:
-				<strong><?xjs write(_mail_tab == 'sent' ? header.from : header.to); ?></strong>
+				<?xjs write(_mail_tab == 'sent' ? locale.strings.page_mail.label_message_to : locale.strings.page_mail.label_message_from); ?>:
+				<strong><?xjs write(_mail_tab == 'sent' ? header.to : header.from); ?></strong>
 				<?xjs write(locale.strings.page_mail.label_message_date); ?>
 				<?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
 				<p>
-- 
GitLab


From 302ebbf5808eccab0483ce83b3b5870cb3495204 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 27 Jan 2019 02:47:35 -0800
Subject: [PATCH 427/752] Fix bug where all Inbox messages were appearing in
 the Sent tab.

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 2cf169c389..6269b1c513 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -289,7 +289,7 @@ function get_mail_headers(filter, ascending) {
                 if (filter == 'spam') ret.headers.push(h);
             } else {
                 h.attr&MSG_READ ? ret.inbox.read++ : (ret.inbox.unread++);
-                if (filter != 'spam') ret.headers.push(h);
+                if (filter == 'inbox') ret.headers.push(h);
             }
         }
     }
-- 
GitLab


From 4dfb58f1b1a434ca773db6c9370e3b947b9bc7cb Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 27 Jan 2019 05:32:34 -0800
Subject: [PATCH 428/752] Fix a couple of issues with telegrams (beeps and HTML
 tags)

I noticed that beep chars (ASCII 7), which come in some
telegrams (e.g. SBBSecho notices) were being printed as a box
glyph in the web UI. Strip those before emitting the telegram event.

I also noticed that some text was missing from some telegrams
(e.g. <user@addr> portion of email notices) - this was because
the <> chars weren't be html-entity-encoded. So now I'm using
html_encode() to HTML-entity encode the string (not the
white-space) and stripping the Ctrl-A codes here.

It would be kind of cool if the colors in the telegrams could
be retained/displayed, but html_encode sets the background of
the text to black, which doesn't look so hot with the current
theme and stuff. We might want to revisit that (e.g. not strip the
Ctrl-A chars here but rather let html_encode() tranlsate them to
HTML color sequences).

Another thing I noticed, if a telegram doesn't end in a CRLF, the
web UI doesn't automatically add/display a <br>. It probably should
(just in case). All telegrams should end in a CRLF, but you never
know.
---
 web/lib/events/telegram.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/web/lib/events/telegram.js b/web/lib/events/telegram.js
index 45ab4b5d9e..f05716bc78 100644
--- a/web/lib/events/telegram.js
+++ b/web/lib/events/telegram.js
@@ -6,7 +6,13 @@ function cycle() {
     if (time() - last_run <= frequency) return;
     last_run = time();
     const tg = system.get_telegram(user.number);
-    if (tg !== null) emit({ event: 'telegram', data: JSON.stringify(tg) });
+    if (tg !== null) emit({ event: 'telegram'
+		, data: JSON.stringify(html_encode(tg.replace('\7', '').replace(/\1./g, '')
+			, /* ex-ascii */true
+			, /* white-space */false
+			, /* ansi */false
+			, /* ctrl_a */false))
+		});
 }
 
 this;
-- 
GitLab


From 85f2f6ed15676f5c1f83cf5e3c3bccb50f578748 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 29 Jan 2019 14:59:17 -0500
Subject: [PATCH 429/752] Home cursor in post body textarea.

---
 web/root/js/forum.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 31a5aa20ca..3e515a98f0 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -104,6 +104,7 @@ function addNew(sub) {
 		$('#newmessage-body').val(
             $('#newmessage-body').val() + '\r\n' + data.signature
 		);
+        $("#newmessage-body")[0].setSelectionRange(0,0);
 	});
 	window.location.hash = '#newmessage';
 	$('#newmessage-body').keydown(function (evt) {
@@ -270,6 +271,8 @@ function addReply(sub, id) {
 	);
 	$.getJSON('./api/forum.ssjs?call=get-signature', function (data) {
 		$('#replytext-' + id).val($('#replytext-' + id).val() + '\r\n' + data.signature);
+        $('#replytext-' + id)[0].setSelectionRange(0,0);
+
 	});
 	$('#replytext-' + id).keydown(function (evt) {
 		evt.stopImmediatePropagation();
-- 
GitLab


From d2a3dbe9e33557084e28c9ff2b350d38ce5ebc8b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 12 Mar 2019 23:24:24 -0700
Subject: [PATCH 430/752] It's possible for a mail message to appear in both
 the inbox and sent folders

... when sending a message to yourself. :-)
---
 web/lib/forum.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 6269b1c513..49d2e81f3a 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -283,7 +283,8 @@ function get_mail_headers(filter, ascending) {
         if (h.from_ext == user.number) {
             h.attr&MSG_READ ? ret.sent.read++ : (ret.sent.unread++);
             if (filter == 'sent') ret.headers.push(h);
-        } else if (h.to_ext == user.number) {
+        }
+	if (h.to_ext == user.number) {
             if ((h.attr&MSG_SPAM) || (h.subject.search(/^SPAM:/) > -1)) {
                 h.attr&MSG_READ ? ret.spam.read++ : (ret.spam.unread++);
                 if (filter == 'spam') ret.headers.push(h);
-- 
GitLab


From 925e4218382b394860a8becf82126b7422a8e210 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 13 Mar 2019 00:23:08 -0700
Subject: [PATCH 431/752] Fix sending a new mail message to a local user

We need to look-up and store the destination user's number in the message
header (in the 'to_ext' field).
---
 web/lib/forum.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 49d2e81f3a..1849280051 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -453,7 +453,9 @@ function postNew(sub, to, subject, body) {
         subject : subject
     };
     if (sub === 'mail') {
-        header.to_net_addr = header.to;
+	header.to_ext = system.matchuser(to);
+	if (header.to_ext === 0)
+		header.to_net_addr = header.to;
         return postMail(header, body);
     } else {
         return postMessage(sub, header, body);
-- 
GitLab


From 2f0ce811dfae05e6764c2cc7d2ecf0f9f6d3ae3c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 13 Mar 2019 10:46:53 -0400
Subject: [PATCH 432/752] New page type .link for adding external links to your
 navbar/menus. Format is one line, two column CSV: url,title eg.
 'web/pages/123-synchronet.link' contains: http://synchro.net/,Synchronet BBS
 Title can't have a comma in it; guess I should fix that. I think this was
 requested by poindexter fortran. The links in the menu aren't actually
 external but will lead to a redirect via index.xjs.  It was just easier that
 way.

---
 web/lib/pages.js   | 54 ++++++++++++++++++---------------
 web/root/index.xjs | 75 ++++++++++++++++++----------------------------
 2 files changed, 59 insertions(+), 70 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index fa6cb98d1d..95c8cb4d51 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -80,30 +80,36 @@ function getPageList(dir) {
 	dir = backslash(fullpath(dir));
 	if (dir.indexOf(settings.web_pages) !== 0) return {};
 
-	var webctrl = getWebCtrl(dir);
-
-	var pages = {};
-	directory(dir + '*').forEach(
-		function (e) {
-			if (file_isdir(e)) {
-				e = fullpath(e);
-                if (e.search(/\.examples.$/) > -1) return;
-				var list = getPageList(e);
-				if (Object.keys(list).length > 0) {
-					pages[e.split(e.substr(-1, 1)).slice(-2, -1)] = list;
-				}
-			} else if (e.search(/(\.xjs\.ssjs|webctrl\.ini)$/i) < 0) {
-				var fn = file_getname(e);
-				var ctrl = getCtrlLine(e);
-				if ((	typeof webctrl === 'undefined' ||
-						webCtrlTest(webctrl, fn)
-					) && !ctrl.options.hidden
-				) {
-					pages[ctrl.title] = file_getname(e);
-				}
-			}
-		}
-	);
+	const webctrl = getWebCtrl(dir);
+
+    const pages = directory(dir + '*').reduce(function (a, c) {
+        if (file_isdir(c)) {
+            const list = getPageList(c);
+            if (Object.keys(list).length) {
+                a[c.split(c.substr(-1, 1)).slice(-2, -1)] = list;
+            }
+            return a;
+        }
+        const ext = file_getext(c).toUpperCase();
+        if (c.search(/(\.xjs\.ssjs|webctrl\.ini)$/i) < 0
+            && ['.HTML', '.SSJS', '.XJS', '.TXT', '.LINK'].indexOf(ext) > -1
+        ) {
+            const fn = file_getname(c);
+            if (webctrl && !webCtrlTest(webctrl, fn)) return a;
+            if (ext == '.LINK') {
+                const f = new File(c);
+                if (!f.open('r')) return a;
+                const l = f.readln().split(',');
+                f.close();
+                if (l.length < 2) return a;
+                a[l[1]] = l[0];
+            } else {
+                const ctrl = getCtrlLine(c);
+                if (!ctrl.options.hidden) a[ctrl.title] = file_getname(c);
+            }
+        }
+        return a;
+    }, {});
 
 	return pages;
 
diff --git a/web/root/index.xjs b/web/root/index.xjs
index ee5d51c8d4..bc1c8cf9e4 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -9,27 +9,20 @@
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
 
-	var page = (
-		(	typeof http_request.query.page === 'undefined' ||
-			!file_exists(
-				fullpath(
-					settings.web_pages + http_request.query.page[0]
-				)
-			) ||
-			fullpath(
-				settings.web_pages + http_request.query.page[0]
-			).indexOf(fullpath(settings.web_pages)) !== 0
-		)
-		? '000-home.xjs'
-		: http_request.query.page[0]
-	);
-
+	var page = typeof http_request.query.page == 'undefined' ? '000-home.xjs' : http_request.query.page[0];
+    if (page.search(/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/i) > -1) {
+        http_reply.status = '301 Moved Permanently';
+        http_reply.header['Location'] = page;
+        exit();
+    }
+
+    if (!file_exists(fullpath(settings.web_pages + page)) || fullpath(settings.web_pages + page).indexOf(fullpath(settings.web_pages)) !== 0) {
+        page = '000-home.xjs';
+    }
 	var page_ctrl = getCtrlLine(settings.web_pages + page);
 
 	function writePage() {
-		var ini = getWebCtrl(
-			settings.web_pages +  '/' + page.replace(file_getname(page), '')
-		);
+		var ini = getWebCtrl(settings.web_pages +  '/' + page.replace(file_getname(page), ''));
 		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
 			write(getPage(page));
 		}
@@ -51,23 +44,17 @@
 
 <?xjs
 	function _menu(obj, path) {
-		Object.keys(obj).forEach(
-			function (e) {
-				if (typeof obj[e] === 'object') {
-					_subMenu(obj[e], e, (path || '') + e + '/');
-				} else {
-?>
-				<li>
-					<a href="./?page=<?xjs write((path || '') + obj[e]); ?>">
-						<?xjs write(e); ?>
-					</a>
-				</li>
-<?xjs
-				}
-			}
-		);
-	}
+		Object.keys(obj).forEach(function (e) {
+			if (typeof obj[e] == 'object') {
+				_subMenu(obj[e], e, (path || '') + e + '/');
+			} else {
 ?>
+    		<li>
+        		<a href="./?page=<?xjs write((path || '') + obj[e]); ?>">
+                    <?xjs write(e); ?>
+                </a>
+    		</li>
+<?xjs }});} ?>
 
 <?xjs function _sidebar() {
 		if (settings.layout_sidebar_off || page_ctrl.options.no_sidebar) return;
@@ -220,18 +207,14 @@
 		<script src="./js/offcanvas.js"></script>
 
     	<script>
-    		$(document).ready(
-    			function () {
-    				$('a.dropdown-toggle').on(
-    					"click", function (e) {
-    						$(this).next('ul').toggle();
-    						$(this).next('div').toggle();
-    						e.stopPropagation();
-    						e.preventDefault();
-    					}
-    				);
-    			}
-    		);
+    		$(document).ready(function () {
+				$('a.dropdown-toggle').on('click', function (e) {
+					$(this).next('ul').toggle();
+					$(this).next('div').toggle();
+					e.stopPropagation();
+					e.preventDefault();
+				});
+			});
     	</script>
 
 	</body>
-- 
GitLab


From 6369a1472aae9f396b6c8f199457d1929ef05e22 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 14 Mar 2019 13:48:16 -0400
Subject: [PATCH 433/752] Set some defaults so that the absence of a page
 control line isn't pants-shittingly bad. Look for the first // comment in an
 SSJS file, don't just assume it will be on the first line.

---
 web/lib/pages.js | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 95c8cb4d51..0a3ec3224d 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -27,16 +27,22 @@ function webCtrlTest(ini, filename) {
 
 function getCtrlLine(file) {
 
-	if (fullpath(file).indexOf(fullpath(settings.web_pages)) !== 0) return;
+	if (fullpath(file).indexOf(fullpath(settings.web_pages)) != 0) return;
 
-	var f = new File(file);
-	var ctrl = '';
-	var ext = file_getext(file).toUpperCase();
+    var ctrl = '';
+	const f = new File(file);
+	const ext = file_getext(file).toUpperCase();
 	switch (ext) {
 		case '.JS':
 		case '.SSJS':
 			if (!f.open('r')) return;
-			ctrl = f.readln().replace(/\/\//g, '');
+            while (!f.eof) {
+                var i = f.readln().match(/\/\/(.*?)$/);
+                if (i !== null && i.length > 1) {
+                    ctrl = i[1];
+                    break;
+                }
+            }
 			f.close();
 			break;
 		case '.XJS':
@@ -66,11 +72,11 @@ function getCtrlLine(file) {
 	var title = ctrl.replace(/^.*\:/, '');
 
 	return {
-		options : {
-			hidden : (opts.indexOf('HIDDEN') >= 0),
-			no_sidebar : (opts.indexOf('NO_SIDEBAR') >= 0)
+		options: {
+			hidden: (opts.indexOf('HIDDEN') >= 0),
+			no_sidebar: (opts.indexOf('NO_SIDEBAR') >= 0)
 		},
-		title : title
+		title: title || file_getname(file)
 	};
 
 }
-- 
GitLab


From 13925bbc4fd78664e8890e43c15cadb76b33f2e5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 14 Mar 2019 13:57:39 -0400
Subject: [PATCH 434/752] Yep

---
 web/lib/pages.js | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 0a3ec3224d..631cbeaf7e 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -27,7 +27,15 @@ function webCtrlTest(ini, filename) {
 
 function getCtrlLine(file) {
 
-	if (fullpath(file).indexOf(fullpath(settings.web_pages)) != 0) return;
+    const ret = {
+        options: {
+            hidden: false,
+            no_sidebar: false
+        },
+        title: file_getname(file)
+    };
+
+	if (fullpath(file).indexOf(fullpath(settings.web_pages)) != 0) return ret;
 
     var ctrl = '';
 	const f = new File(file);
@@ -35,7 +43,7 @@ function getCtrlLine(file) {
 	switch (ext) {
 		case '.JS':
 		case '.SSJS':
-			if (!f.open('r')) return;
+			if (!f.open('r')) return ret;
             while (!f.eof) {
                 var i = f.readln().match(/\/\/(.*?)$/);
                 if (i !== null && i.length > 1) {
@@ -47,7 +55,7 @@ function getCtrlLine(file) {
 			break;
 		case '.XJS':
 		case '.HTML':
-			if (!f.open('r')) return;
+			if (!f.open('r')) return ret;
 			while (!f.eof) {
 				var i = f.readln().match(/\<\!\-\-(.*?)\-\-\>/);
 				if (i !== null && i.length > 1) {
@@ -70,14 +78,11 @@ function getCtrlLine(file) {
 	}
 
 	var title = ctrl.replace(/^.*\:/, '');
+    if (opts.indexOf('HIDDEN') > -1) ret.options.hidden = true;
+    if (opts.indexOf('NO_SIDEBAR') > -1) ret.options.no_sidebar = true;
+    if (title != '') ret.title = title;
 
-	return {
-		options: {
-			hidden: (opts.indexOf('HIDDEN') >= 0),
-			no_sidebar: (opts.indexOf('NO_SIDEBAR') >= 0)
-		},
-		title: title || file_getname(file)
-	};
+    return ret;
 
 }
 
-- 
GitLab


From e7a76f2ec65cad70e592e63dc58cab86f89c84ef Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 9 Apr 2019 11:49:25 -0400
Subject: [PATCH 435/752] Blind and untested attempt to fix submenu name
 parsing on Windows. Android8675 will test this for me.

---
 web/lib/pages.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 631cbeaf7e..462b85e014 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -97,7 +97,7 @@ function getPageList(dir) {
         if (file_isdir(c)) {
             const list = getPageList(c);
             if (Object.keys(list).length) {
-                a[c.split(c.substr(-1, 1)).slice(-2, -1)] = list;
+                a[c.split(backslash(c).substr(-1, 1)).slice(-2, -1)] = list;
             }
             return a;
         }
-- 
GitLab


From 83e76fb4dd70238a062473c664e4513ec1d56b63 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 9 Apr 2019 11:55:07 -0400
Subject: [PATCH 436/752] Possibly fix Windows submenu name parsing.

---
 web/lib/pages.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 462b85e014..6564c76ec4 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -96,8 +96,9 @@ function getPageList(dir) {
     const pages = directory(dir + '*').reduce(function (a, c) {
         if (file_isdir(c)) {
             const list = getPageList(c);
+            const cc = backslash(c);
             if (Object.keys(list).length) {
-                a[c.split(backslash(c).substr(-1, 1)).slice(-2, -1)] = list;
+                a[cc.split(cc.substr(-1, 1)).slice(-2, -1)] = list;
             }
             return a;
         }
-- 
GitLab


From 0b1a99b65988419e9087f1cb198d95f5c2e78efa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 9 May 2019 00:37:12 -0400
Subject: [PATCH 437/752] Rather lazy fetch of directory name for dropdown menu
 naming. Might fix Android8675's problem.

---
 web/lib/pages.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 6564c76ec4..4b4799b7c1 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -93,12 +93,13 @@ function getPageList(dir) {
 
 	const webctrl = getWebCtrl(dir);
 
+	const sep = system.platform.search(/^win/i) == 0 ? '\\' : '/';
+
     const pages = directory(dir + '*').reduce(function (a, c) {
         if (file_isdir(c)) {
             const list = getPageList(c);
-            const cc = backslash(c);
             if (Object.keys(list).length) {
-                a[cc.split(cc.substr(-1, 1)).slice(-2, -1)] = list;
+            	a[c.replace(/\\*\/*$/, '').split(sep).slice(-1)] = list;
             }
             return a;
         }
-- 
GitLab


From cf67554dfbb53e835555458c68ac7d7f6f6f7e56 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 9 May 2019 15:40:39 -0400
Subject: [PATCH 438/752] getPageList returns more detail about items. Items
 are now categorized as list, link, or page. index.xjs amended slightly to
 handle this new format. External links in submenus will now work. External
 links will always open in target _blank.  Can reconsider if this is worth
 making customizable at some later date, but I doubt it. Side note: this is an
 example of why you shouldn't mod index.xjs.

---
 web/lib/pages.js   |  8 +++++---
 web/root/index.xjs | 10 +++++++---
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 4b4799b7c1..45cf07e42d 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -99,7 +99,7 @@ function getPageList(dir) {
         if (file_isdir(c)) {
             const list = getPageList(c);
             if (Object.keys(list).length) {
-            	a[c.replace(/\\*\/*$/, '').split(sep).slice(-1)] = list;
+            	a[c.replace(/\\*\/*$/, '').split(sep).slice(-1)] = { type: 'list', list: list };
             }
             return a;
         }
@@ -115,10 +115,12 @@ function getPageList(dir) {
                 const l = f.readln().split(',');
                 f.close();
                 if (l.length < 2) return a;
-                a[l[1]] = l[0];
+                a[l[1]] = { page: l[0], type: 'link' };
             } else {
                 const ctrl = getCtrlLine(c);
-                if (!ctrl.options.hidden) a[ctrl.title] = file_getname(c);
+                if (!ctrl.options.hidden) {
+                    a[ctrl.title] = { page: file_getname(c), type: 'page' };
+                }
             }
         }
         return a;
diff --git a/web/root/index.xjs b/web/root/index.xjs
index bc1c8cf9e4..8cb6689b62 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -37,7 +37,7 @@
 			<span class="caret"></span>
 		</a>
 		<ul class="dropdown-menu">
-			<?xjs _menu(obj, path); ?>
+			<?xjs _menu(obj.list, path); ?>
 		</ul>
 	</li>
 <?xjs } ?>
@@ -45,12 +45,16 @@
 <?xjs
 	function _menu(obj, path) {
 		Object.keys(obj).forEach(function (e) {
-			if (typeof obj[e] == 'object') {
+			if (obj[e].type == 'list') {
 				_subMenu(obj[e], e, (path || '') + e + '/');
 			} else {
 ?>
     		<li>
-        		<a href="./?page=<?xjs write((path || '') + obj[e]); ?>">
+<?xjs if (obj[e].type == 'link') { ?>
+                <a href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
+<?xjs } else { ?>
+        		<a href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
+<?xjs } ?>
                     <?xjs write(e); ?>
                 </a>
     		</li>
-- 
GitLab


From 11d6ed032a8d47bd438bd792e0587c43a0790be0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 9 May 2019 16:20:04 -0400
Subject: [PATCH 439/752] Formatting. Dump message to a temp file while we're
 at it so that an external thing can pick it up.

---
 web/root/api/github.ssjs | 83 ++++++++++++++++++----------------------
 1 file changed, 37 insertions(+), 46 deletions(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 36a8dab5a9..e72005c773 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -20,20 +20,14 @@
 
 load('sbbsdefs.js');
 load('hmac.js');
-var options = load({}, 'modopts.js', 'github_notify');
-
-load('modopts.js');
-var settings = get_mod_options('web');
-
-load(settings.web_directory + '/lib/init.js');
+const options = load({}, 'modopts.js', 'github_notify');
+load(system.exec_dir + '../web/lib/init.js');
 
 function b2h(str) {
-	return str.split('').map(
-		function (e) {
-			var n = ascii(e).toString(16);
-			return n.length < 2 ? ('0' + n) : n;
-		}
-	).join('');
+	return str.split('').map(function (e) {
+		var n = ascii(e).toString(16);
+		return n.length < 2 ? ('0' + n) : n;
+	}).join('');
 }
 
 function verify_signature(key, payload, hash) {
@@ -41,13 +35,13 @@ function verify_signature(key, payload, hash) {
 }
 
 try {
-	var hash = http_request.header['x-hub-signature'].split('=')[1];
-	var payload = JSON.parse(http_request.post_data);
+	const hash = http_request.header['x-hub-signature'].split('=')[1];
+	const payload = JSON.parse(http_request.post_data);
 	if (typeof options[payload.repository.name] === 'undefined') {
 		throw 'Unknown repository ' + payload.repository.name;
 	}
-	var subs = options[payload.repository.name].split(',');
-	var secret = subs.shift();
+	const subs = options[payload.repository.name].split(',');
+	const secret = subs.shift();
 	if (!verify_signature(secret, http_request.post_data, hash)) {
 		throw 'GitHub signature mismatch';
 	}
@@ -56,37 +50,34 @@ try {
 	exit();
 }
 
-var header = {
-	from : payload.head_commit.author.username,
-	to : 'All',
-	subject : 'Changes to ' + payload.repository.full_name
+const header = {
+	from: payload.head_commit.author.username,
+	to: 'All',
+	subject: 'Changes to ' + payload.repository.full_name
 };
 
-var body = payload.commits.map(
-	function (e) {
-		var ret = [
-			'Commit ID: ' + e.id,
-			'Author: ' + e.author.username,
-		];
-		if (e.added.length > 0) ret.push('Added: ' + e.added.join(', '));
-		if (e.removed.length > 0) ret.push('Removed: ' + e.removed.join(', '));
-		if (e.modified.length > 0) ret.push('Modified: ' + e.modified.join(', '));
-		ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
-		return ret.join('\r\n');
-	}
-);
-body.push('Repository URL: ' + payload.repository.url);
-body = body.join('\r\n\r\n');
+const body = payload.commits.map(function (e) {
+	const ret = [ 'Commit ID: ' + e.id, 'Author: ' + e.author.username ];
+	if (e.added.length) ret.push('Added: ' + e.added.join(', '));
+	if (e.removed.length) ret.push('Removed: ' + e.removed.join(', '));
+	if (e.modified.length) ret.push('Modified: ' + e.modified.join(', '));
+	ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
+	return ret.join('\r\n');
+}).concat('Repository URL: ' + payload.repository.url).join('\r\n\r\n');
 
-subs.forEach(
-	function (sub) {
-		try {
-			var mb = new MsgBase(sub);
-			if (!mb.open()) throw 'Failed to open message base ' + sub;
-			if (!mb.save_msg(header, body)) throw 'Failed to save message';
-			mb.close();
-		} catch (err) {
-			log(LOG_ERR, err);
-		}
+subs.forEach(function (sub) {
+	try {
+		const mb = new MsgBase(sub);
+		if (!mb.open()) throw 'Failed to open message base ' + sub;
+		if (!mb.save_msg(header, body)) throw 'Failed to save message';
+		mb.close();
+	} catch (err) {
+		log(LOG_ERR, err);
 	}
-);
+});
+
+const _tf = new File(system.temp_dir + 'github_notify');
+if (_tf.open('w')) {
+    _tf.write(body);
+    _tf.close();
+}
-- 
GitLab


From a85e4881e69be3fff0c8391c524724a66698ee16 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 9 May 2019 16:34:14 -0400
Subject: [PATCH 440/752] eh

---
 web/root/api/github.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index e72005c773..460bca8582 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -76,7 +76,7 @@ subs.forEach(function (sub) {
 	}
 });
 
-const _tf = new File(system.temp_dir + 'github_notify');
+const _tf = new File(system.data_dir + '/github_notify');
 if (_tf.open('w')) {
     _tf.write(body);
     _tf.close();
-- 
GitLab


From 14135ac2d6e227d51154c06ce0e13dc720b9df3c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 22 May 2019 10:29:43 -0400
Subject: [PATCH 441/752] data.errors may not exist.

---
 web/pages/.examples/000-register.xjs | 48 +++++++++++-----------------
 1 file changed, 19 insertions(+), 29 deletions(-)

diff --git a/web/pages/.examples/000-register.xjs b/web/pages/.examples/000-register.xjs
index 5853d8fed0..d697df6baa 100644
--- a/web/pages/.examples/000-register.xjs
+++ b/web/pages/.examples/000-register.xjs
@@ -202,37 +202,27 @@
 <script type="text/javascript">
 
 	function register() {
-		$.post(
-		 	'./api/register.ssjs',
-			$('#form-register').serialize()
-		).done(
-		 	function (data) {
-		 		if (data.errors.length > 0) {
-		 			$('#errorbox').html('');
-		 			data.errors.forEach(
-		 				function (err) {
-		 					$('#errorbox').append('<p>' + err + '</p>');
-		 				}
-		 			);
-		 			$('#errorbox').attr('hidden', false);
-		 		} else {
-		 			$('#errorbox').attr('hidden', true);
-		 			$('#form-register-container').html(
-		 				'<?xjs write(locale.strings.page_register.message_account_created); ?>'
-		 			);
-		 		}
-		 	}
-		);
+		$.post('./api/register.ssjs', $('#form-register').serialize()).done(function (data) {
+			if (data.errors && data.errors.length > 0) {
+				$('#errorbox').html('');
+				data.errors.forEach(function (err) {
+					$('#errorbox').append('<p>' + err + '</p>');
+				});
+				$('#errorbox').attr('hidden', false);
+			} else {
+				$('#errorbox').attr('hidden', true);
+				$('#form-register-container').html(
+					'<?xjs write(locale.strings.page_register.message_account_created); ?>'
+				);
+			}
+		});
 	}
 
-	$('#form-register').validator().on(
-		'submit',
-		function (evt) {
-			if (evt.isDefaultPrevented()) return;
-			evt.preventDefault();
-			register();
-		}
-	);
+	$('#form-register').validator().on('submit', function (evt) {
+		if (evt.isDefaultPrevented()) return;
+		evt.preventDefault();
+		register();
+	});
 
 	// Traps
 	$('#send-me-free-stuff').attr('hidden', true);
-- 
GitLab


From 71fae1cc0bae808903d555c919a574bf5e30c03c Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 6 Jun 2019 14:47:05 -0400
Subject: [PATCH 442/752] fTelnetScript id attribute is required.

---
 web/pages/.examples/003-games.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index bcc8548b5c..2fa624aea8 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -41,7 +41,7 @@
 	</div>
 </div>
 
-<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
+<?xjs write('<script id="fTelnetScript" src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime() + '"></script>'); ?>
 <script type="text/javascript">
     var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
     var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;
-- 
GitLab


From 92495c215706c3c0a818df561b470db87e5d86b4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 14 Jun 2019 11:36:57 -0400
Subject: [PATCH 443/752] Define an alert(msg) function in case some library
 tries to use it.

---
 web/lib/init.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/web/lib/init.js b/web/lib/init.js
index 3ea18010f9..eeebddb739 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -68,3 +68,9 @@ Object.keys(defaults).forEach(function (e) {
 });
 
 load(settings.web_lib + 'locale.js');
+
+if (typeof alert === 'undefined') {
+    function alert(msg) {
+        log(LOG_ERR, msg);
+    }
+}
-- 
GitLab


From 609cb8b581767a21538a3ccde3672370262e0a8b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 23 Jun 2019 01:42:08 -0400
Subject: [PATCH 444/752] New ctrl/modopts.ini [web] keys, true/false values: -
 ftelnet_rip: load ftelnet variant with RIP support - ftelnet_xfer: load
 ftelnet variant with file transfer support - ftelnet_menubar: display the
 ftelnet menu bar All default to "false" (undefined), so if you want RIP or
 xfers you need to poopoolate these in modopts.ini on the C drive of your
 MS-DOS personal computer system.  This file can be elusive so be sure to
 check behind the pentium manifold. Added get_variant method to ftelnet helper
 lib. Slight change to home.xjs to respeck the ftelnet_menubar option.
 RIP/xfer changes won't take immediate effect after update since I decided to
 cache ftelnet URLs for 24 hours or some shit like that.

---
 web/lib/ftelnet.js               | 9 +++++++--
 web/pages/.examples/000-home.xjs | 3 +++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index 0d2a8ffcee..663ebf7937 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -13,13 +13,18 @@ function get_splash() {
     }
 }
 
+function get_variant() {
+    if (!settings) return 'norip.noxfer';
+    return (settings.ftelnet_rip ? 'rip' : 'norip') + '.' + (settings.ftelnet_xfer ? 'xfer' : 'noxfer');
+}
+
 function get_cached_url(f) {
     if (f.open('r')) {
         const ret = f.read();
         f.close();
         return ret;
     }
-    return 'http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=2018-09-14';
+    return 'http://embed-v2.ftelnet.ca/js/ftelnet-loader.' + get_variant() + '.js?v=2018-09-14';
 }
 
 function get_url() {
@@ -27,7 +32,7 @@ function get_url() {
     const f = new File(system.temp_dir + 'ftelnet.url');
     if (!f.exists || time() - f.date > 86400) {
         try {
-            var str = (new HTTPRequest()).Get('http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.noxfer.js?v=' + (new Date()).getTime());
+            var str = (new HTTPRequest()).Get('http://embed-v2.ftelnet.ca/js/ftelnet-loader.' + get_variant() + '.js?v=' + (new Date()).getTime());
         } catch (err) {
             log(LOG_ERR, 'Failed to fetch fTelnet URL');
             str = '';
diff --git a/web/pages/.examples/000-home.xjs b/web/pages/.examples/000-home.xjs
index e6af12b809..52b98ce1a2 100644
--- a/web/pages/.examples/000-home.xjs
+++ b/web/pages/.examples/000-home.xjs
@@ -6,7 +6,10 @@
         const ftelnet_lib = load({}, settings.web_lib + 'ftelnet.js');
         load('ftelnethelper.js');
 ?>
+    <?xjs if (!settings.ftelnet_menubar) { ?>
         <style>.fTelnetStatusBar { display : none; }</style>
+    <?xjs } ?>
+
     	<div id="fTelnetContainer" class="fTelnetContainer"></div>
         <div class="row">
             <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
-- 
GitLab


From e37cff726ae8c41bc4f632adb544248ce0968e7d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 23 Jun 2019 13:28:14 -0400
Subject: [PATCH 445/752] Don't display redundant connect button if menubar
 enabled.

---
 web/pages/.examples/000-home.xjs | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/web/pages/.examples/000-home.xjs b/web/pages/.examples/000-home.xjs
index 52b98ce1a2..4dfe6ae674 100644
--- a/web/pages/.examples/000-home.xjs
+++ b/web/pages/.examples/000-home.xjs
@@ -6,18 +6,17 @@
         const ftelnet_lib = load({}, settings.web_lib + 'ftelnet.js');
         load('ftelnethelper.js');
 ?>
-    <?xjs if (!settings.ftelnet_menubar) { ?>
-        <style>.fTelnetStatusBar { display : none; }</style>
-    <?xjs } ?>
-
     	<div id="fTelnetContainer" class="fTelnetContainer"></div>
-        <div class="row">
-            <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
-                <button id="ftelnet-connect" class="btn btn-primary">
-                    <?xjs write(locale.strings.page_home.button_ftelnet); ?>
-                </button>
+        <?xjs if (!settings.ftelnet_menubar) { ?>
+            <style>.fTelnetStatusBar { display : none; }</style>
+            <div class="row">
+                <div class="center-block" style="width:200px;margin-bottom:1em;margin-top:1em;">
+                    <button id="ftelnet-connect" class="btn btn-primary">
+                        <?xjs write(locale.strings.page_home.button_ftelnet); ?>
+                    </button>
+                </div>
             </div>
-        </div>
+        <?xjs } ?>
 
     	<script id="fTelnetScript" src="<?xjs write(ftelnet_lib.get_url()); ?>"></script>
     	<script>
@@ -38,9 +37,11 @@
     		Options.ScreenRows = 25;
             Options.SplashScreen = '<?xjs write(ftelnet_lib.get_splash()); ?>';
     		var fTelnet = new fTelnetClient('fTelnetContainer', Options);
-            $('#ftelnet-connect').click(function() {
-                fTelnet.Connect();
-            });
+            if ($('#ftelnet-connect').length) {
+                $('#ftelnet-connect').click(function() {
+                    fTelnet.Connect();
+                });
+            }
     	</script>
 
 <?xjs } ?>
-- 
GitLab


From 32ea8f68331a909d84f7513604cdcc9cf4954a73 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 2 Jul 2019 22:27:46 -0400
Subject: [PATCH 446/752] Comment out seemingly unnecessary response header. 
 Unsure why I did do dat.

---
 web/root/api/auth.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index d3538ee29e..ae124aca58 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -9,6 +9,6 @@ var response = JSON.stringify(
 );
 
 http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = response.length;
+//http_reply.header['Content-Length'] = response.length;
 
 write(response);
-- 
GitLab


From 516708b7766184334bee5a0eed625ab3c308afc6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jul 2019 13:32:32 -0400
Subject: [PATCH 447/752] Fixes #23 / resolves some problems with hacky
 menu/submenu stuff.

---
 web/root/css/style.css |  7 +++----
 web/root/index.xjs     | 28 +++++++++++++++-------------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index b3563f5a0a..6e8cd22b37 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -156,9 +156,8 @@ animation: indicator-fade 3s ease 0s 1 alternate !important;
 .dropdown-submenu {
     position: relative;
 }
-
-.dropdown-submenu .dropdown-menu {
+.dropdown-submenu > .dropdown-menu {
     top: 0;
     left: 100%;
-    margin-top: -1px;
-}
\ No newline at end of file
+    margin-top: -6px;
+} 
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 8cb6689b62..f0c44f8925 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -51,9 +51,9 @@
 ?>
     		<li>
 <?xjs if (obj[e].type == 'link') { ?>
-                <a href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
+                <a class="dropdown-item" href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
 <?xjs } else { ?>
-        		<a href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
+        		<a class="dropdown-item" href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
 <?xjs } ?>
                     <?xjs write(e); ?>
                 </a>
@@ -143,8 +143,8 @@
 									</a>
 								</li>
 							<?xjs } ?>
-							<li class="dropdown">
-								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+							<li class="nav-item dropdown">
+								<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
 									<?xjs write(locale.strings.main.menu_item_login); ?>
 									<span class="caret"></span>
 								</a>
@@ -209,17 +209,19 @@
 		</div>
 
 		<script src="./js/offcanvas.js"></script>
-
     	<script>
-    		$(document).ready(function () {
-				$('a.dropdown-toggle').on('click', function (e) {
-					$(this).next('ul').toggle();
-					$(this).next('div').toggle();
-					e.stopPropagation();
-					e.preventDefault();
-				});
+			$('.dropdown-menu a.dropdown-toggle').on('click', function (e) {
+				if (!$(this).next().hasClass('show')) {
+					$(this).parents('.dropdown-menu').first().find('.show').removeClass("show");
+				}
+			    var $subMenu = $(this).next(".dropdown-menu");
+    			$subMenu.toggleClass('show');
+    			$(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) {
+        			$('.dropdown-submenu .show').removeClass("show");
+    			});
+    			return false;
 			});
-    	</script>
+		</script>
 
 	</body>
 
-- 
GitLab


From c25d0d548b907f81cf6caeb080c0b18c7d66ebbb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 16 Jul 2019 15:05:54 -0400
Subject: [PATCH 448/752] Display to/from net address information on mail page.
 Closes #28.

---
 web/pages/.examples/000-mail.xjs | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index 505b6841dd..8611bfa64e 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -22,8 +22,11 @@
 				</div>
 			</div>
 			<div class="col-sm-11" style="cursor:pointer;" onclick="getMailBody(<?xjs write(header.number); ?>)">
-				<?xjs write(_mail_tab == 'sent' ? locale.strings.page_mail.label_message_to : locale.strings.page_mail.label_message_from); ?>:
-				<strong><?xjs write(_mail_tab == 'sent' ? header.to : header.from); ?></strong>
+                <?xjs write(_mail_tab == 'sent' ? locale.strings.page_mail.label_message_to : locale.strings.page_mail.label_message_from); ?>:
+                <strong><?xjs write(_mail_tab == 'sent' ? header.to : header.from); ?></strong>
+                <?xjs if (header.from_net_type != NET_NONE) { ?>
+                    &lt;<?xjs write(_mail_tab == 'sent' ? (header.to + '@' + header.to_net_addr) : (header.from + '@' + header.from_net_addr)); ?>&gt;
+                <?xjs } ?>
 				<?xjs write(locale.strings.page_mail.label_message_date); ?>
 				<?xjs write((new Date(header.when_written_time * 1000)).toLocaleString()); ?>
 				<p>
@@ -31,7 +34,7 @@
 					<strong><?xjs write(header.subject); ?></strong>
 				</p>
 			</div>
-		</div>
+        </div>
 		<div class="message" id="message-<?xjs write(header.number); ?>" hidden></div>
 	</li>
 <?xjs } ?>
-- 
GitLab


From 8dd2d6193975eca2c7c045036a97d70ef73d7078 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 11:27:13 -0400
Subject: [PATCH 449/752] Ignore stuff that shouldn't go to the repo.

---
 .gitignore | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..bc50a8c6fd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+# Ignore processed XJS files
+*.xjs.ssjs
+
+# Ignore non-example pages
+/web/pages/*
+!/web/pages/.examples
+
+# Ignore non-example sidebar modules
+/web/sidebar/*
+!/web/sidebar/.examples
-- 
GitLab


From 3f4dfe70c50c559205ecfaf4f5a90ee99df14aea Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 17:38:50 -0400
Subject: [PATCH 450/752] Include unprettified file _size when returning list
 of files in directory.

---
 web/lib/files.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/lib/files.js b/web/lib/files.js
index 58d9c8aa37..34e4c90b7f 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -27,6 +27,7 @@ function listDirectories(library) {
 function listFiles(dir) {
 	return (new FileBase(file_area.dir[dir].code)).map(function (df) {
         df.size = df.path ? file_size_str(file_size(df.path)) : 'Unknown';
+        df._size = df.path ? file_size(df.path) : 0;
 		return df;
 	});
 }
-- 
GitLab


From e274d5d314ac1d0075cdc6af9313c66c35bc8c57 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 17:40:48 -0400
Subject: [PATCH 451/752] Added in-place sorting of list of files in a
 directory. Sort by name, size, date uploaded. Requested in #25.

---
 web/pages/.examples/002-files.xjs | 36 ++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index fd33f2bedd..fbec6e132f 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -26,7 +26,7 @@
 	</ol>
 
 	<?xjs function writeFileDetails(file) { ?>
-		<a href="./api/files.ssjs?call=download-file&amp;dir=<?xjs write(http_request.query.dir[0]); ?>&amp;file=<?xjs write(file.name); ?>" target="_blank" class="list-group-item striped">
+		<a href="./api/files.ssjs?call=download-file&amp;dir=<?xjs write(http_request.query.dir[0]); ?>&amp;file=<?xjs write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<?xjs write(file._size); ?>" data-uploaded="<?xjs write(file.uldate); ?>" data-name="<?xjs write(file.name); ?>">
 			<strong><?xjs write(file.name); ?></strong> (<?xjs write(file.size); ?>)
 			<p><em>Uploaded <?xjs write(system.timestr(file.uldate)); ?></em></p>
 			<?xjs if (typeof file.extdesc === 'undefined') { ?>
@@ -39,11 +39,25 @@
 		</a>
 	<?xjs } ?>
 
+	<div class="clearfix" style="margin-bottom:1em;">
+		<div class="dropdown pull-right">
+			<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+				Sort by <span class="caret"></span>
+			</button>
+			<ul class="dropdown-menu dropdown-menu-right">
+				<li class="text-right"><a href="#" onclick="file_sort('data-uploaded', true)">Date Uploaded <span class="glyphicon glyphicon-chevron-up" aria-hidden="true"></span></a></li>
+				<li class="text-right"><a href="#" onclick="file_sort('data-uploaded', false)">Date Uploaded <span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span></a></li>
+				<li class="text-right"><a href="#" onclick="file_sort('data-name', true)">Name <span class="glyphicon glyphicon-chevron-up" aria-hidden="true"></span></a></li>
+				<li class="text-right"><a href="#" onclick="file_sort('data-name', false)">Name <span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span></a></li>
+				<li class="text-right"><a href="#" onclick="file_sort('data-size', true)">Size <span class="glyphicon glyphicon-chevron-up" aria-hidden="true"></span></a></li>
+				<li class="text-right"><a href="#" onclick="file_sort('data-size', false)">Size <span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span></a></li>
+			</ul>
+		</div>
+	</div>
 	<div id="file-list-container" class="list-group">
-		<?xjs listFiles(http_request.query.dir[0]).forEach(writeFileDetails); ?>
+	<?xjs listFiles(http_request.query.dir[0]).forEach(writeFileDetails); ?>
 	</div>
 
-
 <?xjs
 	} else if(
 		typeof http_request.query.library !== 'undefined' &&
@@ -106,3 +120,19 @@
 	</div>
 
 <?xjs } ?>
+
+<script type="text/javascript">
+	function file_sort(field, ascending) {
+		const list = Array.from(document.querySelectorAll('a[data-file-list-element]')).sort((a, b) => {
+			if (a.getAttribute(field) == b.getAttribute(field)) return 0;
+			if (a.getAttribute(field) > b.getAttribute(field)) {
+				return ascending ? 1 : -1
+			} else {
+				return ascending ? -1 : 1
+			}
+		});
+		const flc = document.getElementById('file-list-container');
+		flc.innerHTML = '';
+		list.forEach(e => flc.appendChild(e));
+	}
+</script>
\ No newline at end of file
-- 
GitLab


From 69ff562d9b36d2ef23c64967c68bfe57d481fe68 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 23:30:43 -0400
Subject: [PATCH 452/752] Move writePage to pages lib

---
 web/lib/pages.js   | 7 +++++++
 web/root/index.xjs | 9 +--------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 45cf07e42d..b1ba4c379f 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -178,3 +178,10 @@ function getPage(page) {
 	return ret;
 
 }
+
+function writePage(page) {
+	var ini = getWebCtrl(settings.web_pages +  '/' + page.replace(file_getname(page), ''));
+	if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
+		write(getPage(page));
+	}
+}
\ No newline at end of file
diff --git a/web/root/index.xjs b/web/root/index.xjs
index f0c44f8925..1ecea17043 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -21,13 +21,6 @@
     }
 	var page_ctrl = getCtrlLine(settings.web_pages + page);
 
-	function writePage() {
-		var ini = getWebCtrl(settings.web_pages +  '/' + page.replace(file_getname(page), ''));
-		if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
-			write(getPage(page));
-		}
-	}
-
 ?>
 
 <?xjs function _subMenu(obj, title, path) { ?>
@@ -198,7 +191,7 @@
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><span class="glyphicon glyphicon-tasks"></span><?xjs write(locale.strings.main.label_sidebar); ?></button>
 					</p>
 					</div>
-					<?xjs writePage(); ?>
+					<?xjs writePage(page); ?>
 				</div>
 				<?xjs if (!settings.layout_sidebar_left || settings.layout_sidebar_right) _sidebar(); ?>
 			</div>
-- 
GitLab


From 19046e4142ab6cc0be30256a3f8aad622590da31 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 23:31:05 -0400
Subject: [PATCH 453/752] Helpers for handling http_request.query params

---
 web/lib/init.js | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/web/lib/init.js b/web/lib/init.js
index eeebddb739..afba0a7b04 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -67,6 +67,8 @@ Object.keys(defaults).forEach(function (e) {
   }
 });
 
+defaults = undefined;
+
 load(settings.web_lib + 'locale.js');
 
 if (typeof alert === 'undefined') {
@@ -74,3 +76,18 @@ if (typeof alert === 'undefined') {
         log(LOG_ERR, msg);
     }
 }
+
+// Helpers for http_request
+const Req = {
+    // Query parameter p exists and first instance is of optional type t
+    has_param: function (p, t) {
+        if (!t) return typeof http_request.query[p] != 'undefined';
+        return typeof http_request.query[p] != 'undefined' && typeof http_request.query[p] == t;
+    },
+    // First instance of query parameter p, or undefined
+    get_param: function (p) {
+        if (Array.isArray(http_request.query[p])) {
+            return http_request.query[p][0];
+        }
+    }
+};
-- 
GitLab


From 9637a15e50e0b26bca32e60137a4c00b062e1ae7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 23:32:50 -0400
Subject: [PATCH 454/752] Allow setting an 'active section' of the locale ini,
 let the locale object write strings (locale.write(str[,sec])), shortcuts for
 write(locale.strings[sec][str]).

---
 web/lib/locale/en_us.js | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/web/lib/locale/en_us.js b/web/lib/locale/en_us.js
index 087d14d72b..64cc8a52c8 100644
--- a/web/lib/locale/en_us.js
+++ b/web/lib/locale/en_us.js
@@ -41,6 +41,16 @@ function EN_US(name) {
     });
     Object.defineProperty(this, 'strings', { value: strings });
 
+    var active_section = 'page_main';
+    Object.defineProperty(this, 'section', {
+        get: function () {
+            return active_section;
+        },
+        set: function (s) {
+            active_section = s;
+        }
+    });
+
 }
 
 EN_US.prototype.group_numbers = function (n) {
@@ -53,4 +63,8 @@ EN_US.prototype.group_numbers = function (n) {
     ).split('').reverse().join('') + (d > -1 ? n.substring(d) : '');
 }
 
+EN_US.prototype.write = function (str, sec) {
+    write(this.strings[sec || this.section][str]);
+}
+
 var Locale = EN_US;
-- 
GitLab


From 8df0873791ffc8d1682b369a609d5964ea92d34a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 18 Jul 2019 23:42:15 -0400
Subject: [PATCH 455/752] Allow return-style library use.

---
 web/lib/forum.js | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 1849280051..b7bc22777b 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,19 +1,16 @@
 load(settings.web_lib + 'mime-decode.js');
 
 function listGroups() {
-    var response = [];
-    msg_area.grp_list.forEach(
-        function (grp) {
-            if (grp.sub_list.length < 1) return;
-            response.push(
-                {   index : grp.index,
-                    name : grp.name,
-                    description : grp.description,
-                    sub_count : grp.sub_list.length
-                }
-            );
-        }
-    );
+    const response = [];
+    msg_area.grp_list.forEach(function (grp) {
+        if (grp.sub_list.length < 1) return;
+        response.push({
+            index: grp.index,
+            name: grp.name,
+            description: grp.description,
+            sub_count: grp.sub_list.length
+        });
+    });
     return response;
 }
 
@@ -1071,3 +1068,5 @@ function getMessageThreads(sub, max) {
     return threads;
 
 }
+
+this;
-- 
GitLab


From f14fcddf48f0847a19b6792ac1de869d39124fcb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 00:01:05 -0400
Subject: [PATCH 456/752] Allow return-style library use.

---
 web/lib/auth.js | 87 ++++++++++++++++++++++++++-----------------------
 1 file changed, 47 insertions(+), 40 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 2164aa5313..944141a1ab 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -143,45 +143,52 @@ function authenticate(alias, password) {
 	return usr;
 }
 
-// If someone is trying to log in
-if (typeof http_request.query.username !== 'undefined' &&
-	http_request.query.username[0].length <= LEN_ALIAS &&
-	typeof http_request.query.password != 'undefined' &&
-	http_request.query.password[0].length <= LEN_PASS
-) {
-	var usr = authenticate(
-		http_request.query.username[0],
-		http_request.query.password[0]
-	);
-	if (usr instanceof User) setCookie(usr, randomString(512));
-// If they have a cookie
-} else if(
-	typeof http_request.cookie.synchronet !== 'undefined' &&
-	http_request.cookie.synchronet.some(
-		function (e) {
-		 	return(e.search(/^\d+,\w+$/) != -1);
-		}
-	)
-) {
-	// Verify & update their session, or log them out if requested
-	if (typeof http_request.query.logout === 'undefined') {
-		validateSession(http_request.cookie.synchronet);
-	} else {
-		destroySession(http_request.cookie.synchronet);
-	}
+function is_user() {
+    return user.number > 0 && user.alias != settings.guest;
 }
 
-// If they haven't authenticated as an actual user yet
-if (user.number === 0) {
-
-	// Try to log them in as the guest user
-	var gn = system.matchuser(settings.guest);
-	if (gn > 0) {
-		var gu = new User(gn);
-		login(gu.alias, gu.security.password);
-    gu = undefined;
-	} else {
-		// Otherwise just kill the script, for security's sake
-		exit();
-	}
-}
+(function () {
+    // If someone is trying to log in
+    if (typeof http_request.query.username !== 'undefined' &&
+    	http_request.query.username[0].length <= LEN_ALIAS &&
+    	typeof http_request.query.password != 'undefined' &&
+    	http_request.query.password[0].length <= LEN_PASS
+    ) {
+    	var usr = authenticate(
+    		http_request.query.username[0],
+    		http_request.query.password[0]
+    	);
+    	if (usr instanceof User) setCookie(usr, randomString(512));
+    // If they have a cookie
+    } else if(
+    	typeof http_request.cookie.synchronet !== 'undefined' &&
+    	http_request.cookie.synchronet.some(
+    		function (e) {
+    		 	return(e.search(/^\d+,\w+$/) != -1);
+    		}
+    	)
+    ) {
+    	// Verify & update their session, or log them out if requested
+    	if (typeof http_request.query.logout === 'undefined') {
+    		validateSession(http_request.cookie.synchronet);
+    	} else {
+    		destroySession(http_request.cookie.synchronet);
+    	}
+    }
+
+    // If they haven't authenticated as an actual user yet
+    if (user.number === 0) {
+    	// Try to log them in as the guest user
+    	var gn = system.matchuser(settings.guest);
+    	if (gn > 0) {
+    		var gu = new User(gn);
+    		login(gu.alias, gu.security.password);
+        gu = undefined;
+    	} else {
+    		// Otherwise just kill the script, for security's sake
+    		exit();
+    	}
+    }
+})();
+
+this;
-- 
GitLab


From 501a5f82a85fefdc0b0df76dbdc008dcca83a23f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 00:30:58 -0400
Subject: [PATCH 457/752] Load auth.js return-style

---
 web/root/index.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 1ecea17043..bf409412c4 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -5,7 +5,7 @@
 
 	load('xjs.js');
 	load(settings.web_directory + '/lib/init.js');
-	load(settings.web_lib + 'auth.js');
+	const auth_lib = load({}, settings.web_lib + 'auth.js');
 	load(settings.web_lib + 'pages.js');
 	load(settings.web_lib + 'sidebar.js');
 
-- 
GitLab


From 6bdea9cde23a410fdea8079f3d52f2bbb530dd5a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 00:31:25 -0400
Subject: [PATCH 458/752] Use scoped auth_lib

---
 web/lib/events/nodelist.js | 2 +-
 web/lib/pages.js           | 4 ++--
 web/root/api/system.ssjs   | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index a3f931f61e..2efc678edd 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -50,7 +50,7 @@ function scan_web() {
                 change = true;
             }
         } else {
-            const action = getSessionValue(un, 'action');
+            const action = auth_lib.getSessionValue(un, 'action');
             if (web_state[base] != action) {
                 web_state[base] = action;
                 change = true;
diff --git a/web/lib/pages.js b/web/lib/pages.js
index b1ba4c379f..74925577e5 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -143,7 +143,7 @@ function getPage(page) {
 	if (user.alias != settings.guest) {
 		var ctrl = getCtrlLine(page);
 		if (typeof ctrl !== 'undefined' && !ctrl.options.hidden) {
-			setSessionValue(user.number, 'action', ctrl.title);
+			auth_lib.setSessionValue(user.number, 'action', ctrl.title);
 		}
 	}
 
@@ -184,4 +184,4 @@ function writePage(page) {
 	if ((typeof ini === "boolean" && !ini) || webCtrlTest(ini, page)) {
 		write(getPage(page));
 	}
-}
\ No newline at end of file
+}
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 527d73854b..d099c8a0eb 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -35,7 +35,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				if (usr.alias === settings.guest) continue;
 				if (usr.settings&USER_QUIET) continue;
 				if (usr.logontime < time() - settings.inactivity) continue;
-				var webAction = getSessionValue(usr.number, 'action');
+				var webAction = auth_lib.getSessionValue(usr.number, 'action');
 				if (webAction === null) continue;
 				reply.push({
                     status: '',
@@ -82,7 +82,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
 				break;
 			}
-			setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
+			auth_lib.setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
 			break;
 
 		default:
-- 
GitLab


From 02e30d907cba6c1a9e533dfbae9b896ce946cdfd Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 01:20:27 -0400
Subject: [PATCH 459/752] Some cleanup, use new helpers and locale stuff.

---
 web/pages/.examples/002-files.xjs | 71 ++++++++++++++-----------------
 1 file changed, 31 insertions(+), 40 deletions(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index fbec6e132f..f8efc8b8d2 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -2,39 +2,37 @@
 
 <?xjs
 	load(settings.web_lib + 'files.js');
-
-	if (typeof http_request.query.dir !== 'undefined' &&
-		typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'
-	) {
+    locale.section = 'page_files';
+	if (Req.has_param('dir')) {
 ?>
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
-				<?xjs write(locale.strings.page_files.title); ?>
+			<a href="./?page=<? write(http_request.query.page[0]); ?>">
+				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(file_area.dir[http_request.query.dir[0]].lib_index); ?>">
-				<?xjs write(file_area.dir[http_request.query.dir[0]].lib_name); ?>
+			<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;library=<? write(file_area.dir[http_request.query.dir[0]].lib_index); ?>">
+				<? write(file_area.dir[Req.get_param('dir')].lib_name); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;dir=<?xjs write(http_request.query.dir[0]); ?>">
-				<?xjs write(http_request.query.dir[0]); ?>
+			<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;dir=<? write(http_request.query.dir[0]); ?>">
+				<? write(Req.get_param('dir')); ?>
 			</a>
 		</li>
 	</ol>
 
 	<?xjs function writeFileDetails(file) { ?>
-		<a href="./api/files.ssjs?call=download-file&amp;dir=<?xjs write(http_request.query.dir[0]); ?>&amp;file=<?xjs write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<?xjs write(file._size); ?>" data-uploaded="<?xjs write(file.uldate); ?>" data-name="<?xjs write(file.name); ?>">
-			<strong><?xjs write(file.name); ?></strong> (<?xjs write(file.size); ?>)
-			<p><em>Uploaded <?xjs write(system.timestr(file.uldate)); ?></em></p>
+		<a href="./api/files.ssjs?call=download-file&amp;dir=<? write(Req.get_param('dir')); ?>&amp;file=<? write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<? write(file._size); ?>" data-uploaded="<? write(file.uldate); ?>" data-name="<? write(file.name); ?>">
+			<strong><? write(file.name); ?></strong> (<? write(file.size); ?>)
+			<p><em>Uploaded <? write(system.timestr(file.uldate)); ?></em></p>
 			<?xjs if (typeof file.extdesc === 'undefined') { ?>
-				<p><?xjs write(file.desc); ?></p>
+				<p><? write(file.desc); ?></p>
 			<?xjs } else if (file.extdesc.search(/(\x1B\[|[\xA8-\xFE])/) > -1) { ?>
-				<pre class="ansi"><?xjs write(html_encode(file.extdesc, true, false, true, true)); ?></pre>
+				<pre class="ansi"><? write(html_encode(file.extdesc, true, false, true, true)); ?></pre>
 			<?xjs } else { ?>
-				<pre class="list"><?xjs write(file.extdesc.replace(/[^\r,\n\x20-\x7E]/g, '')); ?></pre>
+				<pre class="list"><? write(file.extdesc.replace(/[^\r,\n\x20-\x7E]/g, '')); ?></pre>
 			<?xjs } ?>
 		</a>
 	<?xjs } ?>
@@ -58,34 +56,29 @@
 	<?xjs listFiles(http_request.query.dir[0]).forEach(writeFileDetails); ?>
 	</div>
 
-<?xjs
-	} else if(
-		typeof http_request.query.library !== 'undefined' &&
-		typeof file_area.lib_list[http_request.query.library[0]] !== 'undefined'
-	) {
-?>
+<?xjs } else if (Req.has_param('library')) { ?>
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
-				<?xjs write(locale.strings.page_files.title); ?>
+			<a href="./?page=<? write(http_request.query.page[0]); ?>">
+				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(http_request.query.library[0]); ?>">
-				<?xjs write(file_area.lib_list[http_request.query.library[0]].name); ?>
+			<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;library=<? write(http_request.query.library[0]); ?>">
+				<? write(file_area.lib_list[http_request.query.library[0]].name); ?>
 			</a>
 		</li>
 	</ol>
 
 
 	<?xjs function writeDirectory(dir) { ?>
-		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;dir=<?xjs write(dir.dir.code); ?>" class="list-group-item striped">
-			<h4><strong><?xjs write(dir.dir.name); ?></strong></h4>
+		<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
+			<h4><strong><? write(dir.dir.name); ?></strong></h4>
 			<p>
-				<?xjs write(dir.dir.description); ?>:
-				<?xjs write(dir.fileCount); ?>
-				<?xjs write(dir.fileCount === 1 ? locale.strings.page_files.stat_suffix_file : locale.strings.page_files.stat_suffix_files); ?>
+				<? write(dir.dir.description); ?>:
+				<? write(dir.fileCount); ?>
+				<? locale.write(dir.fileCount === 1 ? 'stat_suffix_file' : 'stat_suffix_files'); ?>
 			</p>
 		</a>
 	<?xjs } ?>
@@ -98,19 +91,17 @@
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<?xjs write(http_request.query.page[0]); ?>">
-				<?xjs write(locale.strings.page_files.title); ?>
-			</a>
+			<a href="./?page=<? write(http_request.query.page[0]); ?>"><? locale.write('title'); ?></a>
 		</li>
 	</ol>
 
 	<?xjs function writeLibrary(library) { ?>
-		<a href="./?page=<?xjs write(http_request.query.page[0]); ?>&amp;library=<?xjs write(library.index); ?>" class="list-group-item striped">
-			<h3><strong><?xjs write(library.name); ?></strong></h3>
+		<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
+			<h3><strong><? write(library.name); ?></strong></h3>
 			<p>
-				<?xjs write(library.description); ?>:
-				<?xjs write(library.dir_list.length); ?>
-				<?xjs write(library.dir_list.length === 1 ? locale.strings.page_files.stat_suffix_directory : locale.strings.page_files.stat_suffix_directories); ?>
+				<? write(library.description); ?>:
+				<? write(library.dir_list.length); ?>
+				<? locale.write(library.dir_list.length === 1 ? 'stat_suffix_directory' : 'stat_suffix_directories'); ?>
 			</p>
 		</a>
 	<?xjs } ?>
@@ -135,4 +126,4 @@
 		flc.innerHTML = '';
 		list.forEach(e => flc.appendChild(e));
 	}
-</script>
\ No newline at end of file
+</script>
-- 
GitLab


From dcb885a6dc90fbc13aa17e80f181f285b22046e0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 01:25:27 -0400
Subject: [PATCH 460/752] More cleanup.

---
 web/pages/.examples/002-files.xjs | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index f8efc8b8d2..9b3f9468b2 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -7,17 +7,17 @@
 ?>
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? write(http_request.query.page[0]); ?>">
+			<a href="./?page=<? write(Req.get_param('page')); ?>">
 				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;library=<? write(file_area.dir[http_request.query.dir[0]].lib_index); ?>">
+			<a href="./?page=<? write(Req.get_param('page')); ?>&amp;library=<? write(file_area.dir[Req.get_param('dir')].lib_index); ?>">
 				<? write(file_area.dir[Req.get_param('dir')].lib_name); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;dir=<? write(http_request.query.dir[0]); ?>">
+			<a href="./?page=<? write(Req.get_param('page')); ?>&amp;dir=<? write(Req.get_param('dir')); ?>">
 				<? write(Req.get_param('dir')); ?>
 			</a>
 		</li>
@@ -53,27 +53,27 @@
 		</div>
 	</div>
 	<div id="file-list-container" class="list-group">
-	<?xjs listFiles(http_request.query.dir[0]).forEach(writeFileDetails); ?>
+	<?xjs listFiles(Req.get_param('dir')).forEach(writeFileDetails); ?>
 	</div>
 
 <?xjs } else if (Req.has_param('library')) { ?>
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? write(http_request.query.page[0]); ?>">
+			<a href="./?page=<? write(Req.get_param('page')); ?>">
 				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;library=<? write(http_request.query.library[0]); ?>">
-				<? write(file_area.lib_list[http_request.query.library[0]].name); ?>
+			<a href="./?page=<? write(Req.get_param('page')); ?>&amp;library=<? write(Req.get_param('library')); ?>">
+				<? write(file_area.lib_list[Req.get_param('library')].name); ?>
 			</a>
 		</li>
 	</ol>
 
 
 	<?xjs function writeDirectory(dir) { ?>
-		<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
+		<a href="./?page=<? write(Req.get_param('page')); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
 			<h4><strong><? write(dir.dir.name); ?></strong></h4>
 			<p>
 				<? write(dir.dir.description); ?>:
@@ -84,19 +84,19 @@
 	<?xjs } ?>
 
 	<div id="file-list-container" class="list-group">
-		<?xjs listDirectories(http_request.query.library[0]).forEach(writeDirectory); ?>
+		<?xjs listDirectories(Req.get_param('library')).forEach(writeDirectory); ?>
 	</div>
 
 <?xjs } else { ?>
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? write(http_request.query.page[0]); ?>"><? locale.write('title'); ?></a>
+			<a href="./?page=<? write(Req.get_param('page')); ?>"><? locale.write('title'); ?></a>
 		</li>
 	</ol>
 
 	<?xjs function writeLibrary(library) { ?>
-		<a href="./?page=<? write(http_request.query.page[0]); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
+		<a href="./?page=<? write(Req.get_param('page')); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
 			<h3><strong><? write(library.name); ?></strong></h3>
 			<p>
 				<? write(library.description); ?>:
-- 
GitLab


From d71d5f539b20276e11750b9517c3c71ab6800e74 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 01:29:07 -0400
Subject: [PATCH 461/752] Added Req.write_param(p) method.
 write(http_request.query[p][0]) if it exists.

---
 web/lib/init.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/web/lib/init.js b/web/lib/init.js
index afba0a7b04..a1312b3798 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -89,5 +89,10 @@ const Req = {
         if (Array.isArray(http_request.query[p])) {
             return http_request.query[p][0];
         }
+    },
+    write_param: function (p) {
+        if (Array.isArray(http_request.query[p])) {
+            write(http_request.query[p][0]);
+        }
     }
 };
-- 
GitLab


From e737463f476a5da9fc675a2037fe71011786e752 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 01:30:50 -0400
Subject: [PATCH 462/752] Regarding that 'if it exists' thing, check that it's
 an Array and that it has length.

---
 web/lib/init.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index a1312b3798..5796073244 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -86,12 +86,12 @@ const Req = {
     },
     // First instance of query parameter p, or undefined
     get_param: function (p) {
-        if (Array.isArray(http_request.query[p])) {
+        if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
             return http_request.query[p][0];
         }
     },
     write_param: function (p) {
-        if (Array.isArray(http_request.query[p])) {
+        if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
             write(http_request.query[p][0]);
         }
     }
-- 
GitLab


From 624675b3b328ca87208952a229da4e49c5bb7443 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 01:32:20 -0400
Subject: [PATCH 463/752] Use Req.write_param.

---
 web/pages/.examples/002-files.xjs | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index 9b3f9468b2..90dff69e2d 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -7,24 +7,24 @@
 ?>
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? write(Req.get_param('page')); ?>">
+			<a href="./?page=<? Req.write_param('page'); ?>">
 				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? write(Req.get_param('page')); ?>&amp;library=<? write(file_area.dir[Req.get_param('dir')].lib_index); ?>">
+			<a href="./?page=<? Req.write_param('page'); ?>&amp;library=<? write(file_area.dir[Req.get_param('dir')].lib_index); ?>">
 				<? write(file_area.dir[Req.get_param('dir')].lib_name); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? write(Req.get_param('page')); ?>&amp;dir=<? write(Req.get_param('dir')); ?>">
-				<? write(Req.get_param('dir')); ?>
+			<a href="./?page=<? Req.write_param('page'); ?>&amp;dir=<? Req.write_param('dir'); ?>">
+				<? Req.write_param('dir'); ?>
 			</a>
 		</li>
 	</ol>
 
 	<?xjs function writeFileDetails(file) { ?>
-		<a href="./api/files.ssjs?call=download-file&amp;dir=<? write(Req.get_param('dir')); ?>&amp;file=<? write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<? write(file._size); ?>" data-uploaded="<? write(file.uldate); ?>" data-name="<? write(file.name); ?>">
+		<a href="./api/files.ssjs?call=download-file&amp;dir=<? Req.write_param('dir'); ?>&amp;file=<? write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<? write(file._size); ?>" data-uploaded="<? write(file.uldate); ?>" data-name="<? write(file.name); ?>">
 			<strong><? write(file.name); ?></strong> (<? write(file.size); ?>)
 			<p><em>Uploaded <? write(system.timestr(file.uldate)); ?></em></p>
 			<?xjs if (typeof file.extdesc === 'undefined') { ?>
@@ -60,12 +60,12 @@
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? write(Req.get_param('page')); ?>">
+			<a href="./?page=<? Req.write_param('page'); ?>">
 				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? write(Req.get_param('page')); ?>&amp;library=<? write(Req.get_param('library')); ?>">
+			<a href="./?page=<? Req.write_param('page'); ?>&amp;library=<? Req.write_param('library'); ?>">
 				<? write(file_area.lib_list[Req.get_param('library')].name); ?>
 			</a>
 		</li>
@@ -73,7 +73,7 @@
 
 
 	<?xjs function writeDirectory(dir) { ?>
-		<a href="./?page=<? write(Req.get_param('page')); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
+		<a href="./?page=<? Req.write_param('page'); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
 			<h4><strong><? write(dir.dir.name); ?></strong></h4>
 			<p>
 				<? write(dir.dir.description); ?>:
@@ -91,12 +91,12 @@
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? write(Req.get_param('page')); ?>"><? locale.write('title'); ?></a>
+			<a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
 		</li>
 	</ol>
 
 	<?xjs function writeLibrary(library) { ?>
-		<a href="./?page=<? write(Req.get_param('page')); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
+		<a href="./?page=<? Req.write_param('page'); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
 			<h3><strong><? write(library.name); ?></strong></h3>
 			<p>
 				<? write(library.description); ?>:
-- 
GitLab


From 7c5019583ef8e3abca365ddfe506c95c18e6c646 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 01:36:18 -0400
Subject: [PATCH 464/752] Early work on Forum rewrite. XJS-based, easier to
 maintain (and make sense of). This is a hidden page by default for the time
 being. Don't use it; it doesn't do much.

---
 web/pages/.examples/001-forum.xjs | 48 +++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 web/pages/.examples/001-forum.xjs

diff --git a/web/pages/.examples/001-forum.xjs b/web/pages/.examples/001-forum.xjs
new file mode 100644
index 0000000000..affb9005ca
--- /dev/null
+++ b/web/pages/.examples/001-forum.xjs
@@ -0,0 +1,48 @@
+<!--HIDDEN:New Forum-->
+
+<script type="text/javascript" src="./js/forum.js"></script>
+
+<?xjs
+    const forum_lib = load({}, settings.web_lib + 'forum.js');
+    locale.section = 'page_forum';
+?>
+
+<?xjs if (Req.has_param('sub', 'string') && Req.has_param('thread', 'number')) { ?>
+
+<?xjs } else if (Req.has_param('sub', 'string')) { ?>
+
+<?xjs } else if (Req.has_param('group', 'number')) { ?>
+
+<?xjs } else { ?>
+
+    <ol class="breadcrumb">
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+        </li>
+    </ol>
+
+    <div id="forum-list-container" class="list-group">
+        <?xjs const groups = forum_lib.listGroups(); ?>
+        <?xjs groups.forEach(function (e) { ?>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(e.index); ?>" class="list-group-item striped">
+                <h3><strong><? write(e.name); ?></strong></h3>
+                <span title="Unread messages (other)" class="badge ignored" id="badge-ignored-<? write(e.index); ?>"></span>
+                <span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-<? write(e.index); ?>"></span>
+                <p><? write(e.description); ?>: <? write(e.sub_count); ?> <? locale.write('sub_boards'); ?></p>
+            </a>
+        <?xjs }); ?>
+    </div>
+
+    <?xjs if (auth_lib.is_user()) { ?>
+        <script type="text/javascript">
+            (function () {
+                const qs = '<? write(groups.map(function (e) { return e.index; }).join("&group=")); ?>';
+                setInterval(function () {
+                    getGroupUnreadCount(qs);
+                }, <? write(settings.refresh_interval || 60000); ?>);
+                getGroupUnreadCount(qs);
+            })();
+        </script>
+    <?xjs } ?>
+
+<?xjs } ?>
-- 
GitLab


From a22364e44cd1730b96779c832af9ff37609167a2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 13:57:42 -0400
Subject: [PATCH 465/752] Allow additional parameters in call to events.ssjs.
 Return-style load of web/lib/auth.ssjs.

---
 web/root/api/events.ssjs               |  6 ++---
 web/root/js/common.js                  | 32 ++++++++++++++++++--------
 web/sidebar/.examples/001-nodelist.xjs |  6 ++---
 3 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 0758020612..bc73e9029d 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,8 +1,8 @@
 load('sbbsdefs.js');
 load('modopts.js');
-var settings = get_mod_options('web');
+const settings = get_mod_options('web');
 load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
+const auth_lib = load({}, settings.web_lib + 'auth.js');
 
 http_reply.header['Cache-Control'] = 'no-cache';
 http_reply.header['Content-type'] = 'text/event-stream';
@@ -35,7 +35,7 @@ if (file_isdir(settings.web_lib + 'events')) {
             try {
                 if (file_exists(script)) callbacks[e] = load({}, script);
             } catch (err) {
-                log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
+               log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
             }
         });
     }
diff --git a/web/root/js/common.js b/web/root/js/common.js
index 53151221af..f7812e70a6 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -59,6 +59,17 @@ function sendTelegram(alias) {
 	$('#popUpModal').modal('show');
 }
 
+function registerEventListener(scope, callback, params) {
+	params = Object.keys(params || {}).reduce(function (a, c) {
+		a += '&' + c + '=' + params[c];
+		return a;
+	}, '');
+	_sbbs_events[scope] = {
+		qs: 'subscribe=' + scope + params,
+		callback: callback
+	};
+}
+
 window.onload =	function () {
 
 	$('#button-logout').click(logout);
@@ -79,14 +90,14 @@ window.onload =	function () {
 
 	if ($('#button-logout').length > 0) {
 
-        _sbbs_events.mail = function (e) {
+		registerEventListener('mail', function (e) {
             const data = JSON.parse(e.data);
             if (typeof data.count != 'number') return;
             $('#badge-unread-mail').text(data.count < 1 ? '' : data.count);
             $('#badge-unread-mail-inner').text(data.count < 1 ? '' : data.count);
-        }
-
-        _sbbs_events.telegram = function (e) {
+		});
+		
+		registerEventListener('telegram', function (e) {
             const tg = JSON.parse(e.data).replace(/\1./g, '').replace(
                 /\r?\n/g, '<br>'
             );
@@ -94,16 +105,17 @@ window.onload =	function () {
             $('#popUpModalBody').append(tg);
             $('#popUpModalActionButton').hide();
             $('#popUpModal').modal('show');
-        }
+		});
 
 	}
 
-    const _evtqs = Object.keys(_sbbs_events).reduce(function (a, c, i) {
-        return a + (i == 0 ? '?' : '&') + 'subscribe=' + c; }, ''
-    );
-    const _es = new EventSource('/api/events.ssjs' + _evtqs);
+	const qs = Object.keys(_sbbs_events).reduce(function (a, c, i) {
+		return a + (i == 0 ? '?' : '&') + _sbbs_events[c].qs;
+	}, '');
+
+    const es = new EventSource('/api/events.ssjs' + qs);
     Object.keys(_sbbs_events).forEach(function (e) {
-        _es.addEventListener(e, _sbbs_events[e]);
+        es.addEventListener(e, _sbbs_events[e].callback);
     });
 
 }
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 6a9a352fcf..395728f42f 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -80,7 +80,7 @@
         $('#sidebar').find('.sb-nodes-available').text(nll - niu);
     }
 
-    _sbbs_events.nodelist = _sb_nodelist;
+    registerEventListener('nodelist', _sb_nodelist);
 
     <?xjs if (settings.nodelist_ibbs) { ?>
         function _send_ibbs_telegram(sys, host, user) {
@@ -109,7 +109,7 @@
         	$('#popUpModal').modal('show');
         }
 
-        _sbbs_events.sbbsimsg = function (e) {
+        registerEventListener('sbbsimsg', function (e) {
             const data = JSON.parse(e.data);
             var users = 0;
             $('#sbbsimsg-nodelist').addClass('hidden');
@@ -145,7 +145,7 @@
                 $('#sbbsimsg-nodelist').removeClass('hidden');
                 $('#sbbs-nodelist').parent().removeClass('hidden');
             }
-        }
+        });
     <?xjs } ?>
 
 </script>
-- 
GitLab


From 6ca4b048c23570cf81017c995fbd5a2b2e869075 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:30:25 -0400
Subject: [PATCH 466/752] Fix "count is undefined" Pass group index to
 getGroupUnreadCount, not the whole grp obj.

---
 web/lib/forum.js | 36 +++++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index b7bc22777b..8e6cdd34d6 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -103,22 +103,34 @@ function getSubUnreadCount(sub) {
     return ret;
 }
 
+function getSubUnreadCounts() {
+    return msg_area.grp_list[group].sub_list.reduce(function (a, c) {
+        a[c.code] = getSubUnreadCount(c.code);
+        return a;
+    }, {});
+}
+
 function getGroupUnreadCount(group) {
     var ret = {
         scanned : 0,
         total : 0
     };
-    if (typeof msg_area.grp_list[group] === 'undefined') return count;
-    msg_area.grp_list[group].sub_list.forEach(
-        function (sub) {
-            var count = getSubUnreadCount(sub.code);
-            ret.scanned += count.scanned;
-            ret.total += count.total;
-        }
-    );
+    if (typeof msg_area.grp_list[group] === 'undefined') return ret;
+    msg_area.grp_list[group].sub_list.forEach(function (sub) {
+        var count = getSubUnreadCount(sub.code);
+        ret.scanned += count.scanned;
+        ret.total += count.total;
+    });
     return ret;
 }
 
+function getGroupUnreadCounts() {
+    return msg_area.grp_list.reduce(function (a, c) {
+        a[c.index] = getGroupUnreadCount(c.index);
+        return a;
+    }, {});
+}
+
 function getUnreadInThread(sub, thread) {
     if (typeof thread === 'number') {
         var threads = getMessageThreads(sub, settings.max_messages);
@@ -126,11 +138,9 @@ function getUnreadInThread(sub, thread) {
         thread = threads.thread[thread];
     }
     var count = 0;
-    Object.keys(thread.messages).forEach(
-        function (m) {
-            if (thread.messages[m].number > msg_area.sub[sub].scan_ptr) count++;
-        }
-    );
+    Object.keys(thread.messages).forEach(function (m) {
+        if (thread.messages[m].number > msg_area.sub[sub].scan_ptr) count++;
+    });
     return count;
 }
 
-- 
GitLab


From 8ce299a67d0d1e1604f2d1cb48608aabd71182d0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:31:12 -0400
Subject: [PATCH 467/752] Remove param type checks for now; they're all
 strings.

---
 web/lib/init.js | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index 5796073244..6594453d29 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -79,20 +79,19 @@ if (typeof alert === 'undefined') {
 
 // Helpers for http_request
 const Req = {
-    // Query parameter p exists and first instance is of optional type t
-    has_param: function (p, t) {
-        if (!t) return typeof http_request.query[p] != 'undefined';
-        return typeof http_request.query[p] != 'undefined' && typeof http_request.query[p] == t;
-    },
-    // First instance of query parameter p, or undefined
-    get_param: function (p) {
-        if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
-            return http_request.query[p][0];
-        }
-    },
-    write_param: function (p) {
-        if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
-            write(http_request.query[p][0]);
-        }
+  // Query parameter p exists and first instance is of optional type t
+  has_param: function (p) {
+    return (Array.isArray(http_request.query[p]) && http_request.query[p].length);
+  },
+  // First instance of query parameter p, or undefined
+  get_param: function (p) {
+    if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
+        return http_request.query[p][0];
+    }
+  },
+  write_param: function (p) {
+    if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
+        write(http_request.query[p][0]);
     }
+  }
 };
-- 
GitLab


From 0ebde719d7e0ce2b350fca17440e096314386262 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:39:11 -0400
Subject: [PATCH 468/752] getSubUnreadCount needs to know which group

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 8e6cdd34d6..7d47bce0c5 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -103,7 +103,7 @@ function getSubUnreadCount(sub) {
     return ret;
 }
 
-function getSubUnreadCounts() {
+function getSubUnreadCounts(group) {
     return msg_area.grp_list[group].sub_list.reduce(function (a, c) {
         a[c.code] = getSubUnreadCount(c.code);
         return a;
-- 
GitLab


From 5351a9637158888273e4b2135b21af70ac365114 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:40:48 -0400
Subject: [PATCH 469/752] Added onGroupUnreadCount and onSubUnreadCount to
 handle unread counter data when it comes in (whether via poll or
 eventsource). Moved logic out of getGroupUnreadCount and getSubUnreadCount,
 these will be removed at some later date.

---
 web/root/js/forum.js | 54 ++++++++++++++++++++++++--------------------
 1 file changed, 29 insertions(+), 25 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 3e515a98f0..025fb407cc 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -279,37 +279,41 @@ function addReply(sub, id) {
 	});
 }
 
+function onSubUnreadCount(data) {
+	for (sub in data) {
+		if (data[sub].scanned > 0) {
+			$('#badge-' + sub).text(data[sub].total);
+		} else if (data[sub].total > 0) {
+			$('#badge-' + sub).text(data[sub].total);
+		} else {
+			$('#badge-' + sub).text('');
+		}
+	}
+}
+
 // 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
 function getSubUnreadCount(sub) {
-	$.getJSON('./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub, function (data) {
-		for (sub in data) {
-			if (data[sub].scanned > 0) {
-				$('#badge-' + sub).text(data[sub].total);
-			} else if (data[sub].total > 0) {
-				$('#badge-' + sub).text(data[sub].total);
-			} else {
-				$('#badge-' + sub).text('');
-			}
-		}
-	});
+	$.getJSON('./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub, onSubUnreadCount);
+}
+
+function onGroupUnreadCount(data) {
+	for (group in data) {
+		$('#badge-scanned-' + group).text(
+			(data[group].scanned == 0 ? "" : data[group].scanned)
+		);
+		$('#badge-ignored-' + group).text(
+			(	data[group].total == 0 ||
+				data[group].total == data[group].scanned
+				? ''
+				: (data[group].total - data[group].scanned)
+			)
+		);
+	}
 }
 
 // 'group' can be a single group index, or a string of 0&group=1&group=2...
 function getGroupUnreadCount(group) {
-	$.getJSON('./api/forum.ssjs?call=get-group-unread-count&group=' + group, function (data) {
-		for (group in data) {
-			$('#badge-scanned-' + group).text(
-				(data[group].scanned == 0 ? "" : data[group].scanned)
-			);
-			$('#badge-ignored-' + group).text(
-				(	data[group].total == 0 ||
-					data[group].total == data[group].scanned
-					? ''
-					: (data[group].total - data[group].scanned)
-				)
-			);
-		}
-	});
+	$.getJSON('./api/forum.ssjs?call=get-group-unread-count&group=' + group, onGroupUnreadCount);
 }
 
 /*  Fetch a private mail message's body (with links to attachments) where 'id'
-- 
GitLab


From fac16e692746d7d18481132b95ded135fe76d14a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:42:29 -0400
Subject: [PATCH 470/752] EventSource module for Forum. Currently pushes
 group/sub msg unread counts at intervals depending on what was requested upon
 subscription. Can later be used to push new messages (and trigger
 notifications) or vote data if some comes in while looking at a thread/thread
 list.

---
 web/lib/events/forum.js | 56 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 web/lib/events/forum.js

diff --git a/web/lib/events/forum.js b/web/lib/events/forum.js
new file mode 100644
index 0000000000..a237062e35
--- /dev/null
+++ b/web/lib/events/forum.js
@@ -0,0 +1,56 @@
+const forum_lib = load({}, settings.web_lib + 'forum.js');
+
+var last_subs;
+var last_groups;
+var last_run = 0;
+const frequency = (settings.refresh_interval || 60000) / 1000;
+
+// Where 'a' is the previous data and 'b' is new
+function shallow_diff(a, b) {
+    const ret = {};
+    Object.keys(a).forEach(function (e) {
+        if (typeof b[e] == 'undefined') {
+            ret[e] = b[e];
+        } else if (a[e].scanned != b[e].scanned || a[e].total != b[e].total) {
+            ret[e] = b[e];
+        }
+    });
+    return Object.keys(ret).length ? ret : undefined;
+}
+
+function forum_emit(evt, data) {
+    emit({ event: 'forum', data: JSON.stringify({ type: evt, data: data }) });
+}
+
+function scan_groups() {
+    const scan = forum_lib.getGroupUnreadCounts();
+    if (!last_groups) {
+        forum_emit('groups_unread', scan);
+        emit({ event: 'forum', data: JSON.stringify(scan) });
+    } else {
+        const diff = shallow_diff(last_groups, scan);
+        if (diff) forum_emit('groups_unread', scan);
+    }
+    last_groups = scan;
+}
+
+function scan_subs() {
+    const scan = forum_lib.getSubUnreadCounts(Req.get_param('subs_unread'));
+    if (!last_subs) {
+        forum_emit('subs_unread', scan);
+    } else {
+        const diff = shallow_diff(last_subs, scan);
+        if (diff) forum_emit('subs_unread', scan);
+    }
+    last_subs = scan;
+}
+
+function cycle() {
+    if (!auth_lib.is_user()) return;
+    if (time() - last_run <= frequency) return;
+    last_run = time();
+    if (Req.has_param('groups_unread')) scan_groups();
+    if (Req.has_param('subs_unread')) scan_subs();
+}
+
+this;
-- 
GitLab


From 73f106cd7e5a97c70d5265afdbb2c7d60ac20cf0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:46:08 -0400
Subject: [PATCH 471/752] Removed unread polling, switched to forum eventsource
 instead. Simpler checking for query params. Added sub-board listing.

---
 web/pages/.examples/001-forum.xjs | 68 +++++++++++++++++++++++++------
 1 file changed, 56 insertions(+), 12 deletions(-)

diff --git a/web/pages/.examples/001-forum.xjs b/web/pages/.examples/001-forum.xjs
index affb9005ca..5b008f1cb4 100644
--- a/web/pages/.examples/001-forum.xjs
+++ b/web/pages/.examples/001-forum.xjs
@@ -7,11 +7,57 @@
     locale.section = 'page_forum';
 ?>
 
-<?xjs if (Req.has_param('sub', 'string') && Req.has_param('thread', 'number')) { ?>
+<?xjs if (Req.has_param('sub') && Req.has_param('thread')) { ?>
 
-<?xjs } else if (Req.has_param('sub', 'string')) { ?>
+<?xjs } else if (Req.has_param('sub')) { ?>
 
-<?xjs } else if (Req.has_param('group', 'number')) { ?>
+    <ol class="breadcrumb">
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+        </li>
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>"><? write(msg_area.grp_list[Req.get_param('group')].name); ?></a>
+        </li>
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>&sub=<? Req.write_param('sub'); ?>"><? write(msg_area.sub[Req.get_param('sub')].name); ?></a>
+        </li>
+    </ol>
+
+    <div id="forum-list-container" class="list-group">
+
+    </div>
+
+<?xjs } else if (Req.has_param('group')) { ?>
+
+    <ol class="breadcrumb">
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+        </li>
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>"><? write(msg_area.grp_list[Req.get_param('group')].name); ?></a>
+        </li>
+    </ol>
+
+    <div id="forum-list-container" class="list-group">
+        <?xjs const subs = forum_lib.listSubs(Req.get_param('group')); ?>
+        <?xjs subs.forEach(function (e) { ?>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(e.index); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
+                <h4><strong><? write(e.name); ?></strong></h4>
+                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(e.scan_cfg&SCAN_CFG_NEW || e.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : 'total'); ?>" id="badge-<? write(e.code); ?>"></span>
+                <p><? write(e.description); ?></p>
+            </a>
+        <?xjs }); ?>
+    </div>
+
+    <?xjs if (auth_lib.is_user()) { ?>
+        <script type="text/javascript">
+            registerEventListener('forum', function (e) {
+                const data = JSON.parse(e.data);
+                if (data.type != 'subs_unread') return;
+                onSubUnreadCount(data.data);
+             }, { subs_unread: '<? Req.write_param('group'); ?>' });
+        </script>
+    <?xjs } ?>
 
 <?xjs } else { ?>
 
@@ -26,8 +72,8 @@
         <?xjs groups.forEach(function (e) { ?>
             <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(e.index); ?>" class="list-group-item striped">
                 <h3><strong><? write(e.name); ?></strong></h3>
-                <span title="Unread messages (other)" class="badge ignored" id="badge-ignored-<? write(e.index); ?>"></span>
-                <span title="Unread messages (scanned subs)" class="badge scanned" id="badge-scanned-<? write(e.index); ?>"></span>
+                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge ignored" id="badge-ignored-<? write(e.index); ?>"></span>
+                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge scanned" id="badge-scanned-<? write(e.index); ?>"></span>
                 <p><? write(e.description); ?>: <? write(e.sub_count); ?> <? locale.write('sub_boards'); ?></p>
             </a>
         <?xjs }); ?>
@@ -35,13 +81,11 @@
 
     <?xjs if (auth_lib.is_user()) { ?>
         <script type="text/javascript">
-            (function () {
-                const qs = '<? write(groups.map(function (e) { return e.index; }).join("&group=")); ?>';
-                setInterval(function () {
-                    getGroupUnreadCount(qs);
-                }, <? write(settings.refresh_interval || 60000); ?>);
-                getGroupUnreadCount(qs);
-            })();
+            registerEventListener('forum', function (e) {
+                const data = JSON.parse(e.data);
+                if (data.type != 'groups_unread') return;
+                onGroupUnreadCount(data.data);
+            }, { groups_unread: null });
         </script>
     <?xjs } ?>
 
-- 
GitLab


From ebf50abee797ad78725c0c858e76b80ec3e7a504 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 15:52:54 -0400
Subject: [PATCH 472/752] Some new strings for the new Forum.

---
 web/lib/locale/en_us.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web/lib/locale/en_us.ini b/web/lib/locale/en_us.ini
index 0b6f5b4ed0..6db39b965a 100644
--- a/web/lib/locale/en_us.ini
+++ b/web/lib/locale/en_us.ini
@@ -66,6 +66,7 @@ stat_suffix_directories = directories
 
 [page_forum]
 title = XJS Test Forum
+sub_boards = sub-boards
 label_thread_from = By
 label_message_from = From
 label_message_to = To
@@ -75,6 +76,7 @@ label_thread_latest_reply = Latest reply by
 badge_poll = POLL
 badge_downvotes = Downvotes - Parent / Thread Total
 badge_upvotes = Upvotes - Parent / Thread Total
+badge_unread_messages = Unread messages
 button_post_message = Post a new message
 button_post_poll = Post a new poll
 button_scan_new = Include in new message scan
-- 
GitLab


From ac12bcf0f7f81acd3a75664a7cc599d0704b69c5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 23:02:00 -0400
Subject: [PATCH 473/752] Count unread messages while building thread lists if
 a real user is logged in.

---
 web/lib/forum.js | 52 +++++++++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 23 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 7d47bce0c5..66bc807790 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -892,6 +892,9 @@ function getMessageThreads(sub, max) {
         if (header.when_written_time > threads.thread[thread_id].newest) {
             threads.thread[thread_id].newest = header.when_written_time;
         }
+        if (auth_lib.is_user() && header.number > msg_area.sub[sub].scan_ptr) {
+            threads.thread[thread_id].unread++;
+        }
         threads.thread[thread_id].messages[header.number] = {
             attr : header.attr,
             auxattr : header.auxattr,
@@ -998,14 +1001,15 @@ function getMessageThreads(sub, max) {
                     addToThread(headers[h].thread_id, headers[h], subject);
                 } else {
                     threads.thread[headers[h].thread_id] = {
-                        id : headers[h].thread_id,
+                        id: headers[h].thread_id,
                         newest : 0,
-                        subject : headers[h].subject,
-                        messages : {},
-                        votes : {
-                            up : 0,
-                            down : 0
-                        }
+                        subject: headers[h].subject,
+                        messages: {},
+                        votes: {
+                            up: 0,
+                            down: 0
+                        },
+                        unread: 0
                     };
                     addToThread(headers[h].thread_id, headers[h], subject);
                 }
@@ -1030,14 +1034,15 @@ function getMessageThreads(sub, max) {
                     }
                     if (!threaded) {
                         threads.thread[headers[h].thread_back] = {
-                            id : headers[h].thread_back,
-                            newest : 0,
-                            subject : headers[h].subject,
-                            messages : {},
-                            votes : {
-                                up : 0,
-                                down : 0
-                            }
+                            id: headers[h].thread_back,
+                            newest: 0,
+                            subject: headers[h].subject,
+                            messages: {},
+                            votes: {
+                                up: 0,
+                                down: 0
+                            },
+                            unread: 0
                         };
                         addToThread(
                             headers[h].thread_back,
@@ -1050,14 +1055,15 @@ function getMessageThreads(sub, max) {
             } else {
 
                 threads.thread[headers[h].number] = {
-                    id : headers[h].number,
-                    newest : 0,
-                    subject : headers[h].subject,
-                    messages : {},
-                    votes : {
-                        up : 0,
-                        down : 0
-                    }
+                    id: headers[h].number,
+                    newest: 0,
+                    subject: headers[h].subject,
+                    messages: {},
+                    votes: {
+                        up: 0,
+                        down: 0
+                    },
+                    unread: 0
                 };
                 addToThread(headers[h].number, headers[h], subject);
 
-- 
GitLab


From 712c6bd729fc207539826490b806911b44237d21 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 23:04:26 -0400
Subject: [PATCH 474/752] Return-style load of lib/auth.js. Maybe I should just
 start using require() all over the place.

---
 web/root/api/forum.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index ebd39d0f7b..457eb66991 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -6,8 +6,8 @@
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'init.js');
+const auth_lib = load({}, settings.web_lib + 'auth.js');
 load(settings.web_lib + 'forum.js');
 
 var reply = {};
-- 
GitLab


From b865821c7459b5a04b3fef63bdd749f4ff25b1a6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 19 Jul 2019 23:05:37 -0400
Subject: [PATCH 475/752] Thread listing. This is the view of a sub-board, and
 still needs: - Post a new message (start a new thread) - Post a new poll -
 Buttons for scan settings - Pagination / load on scroll (user toggle and/or
 sysop configurable) - Sortation (if in paginated view)

---
 web/pages/.examples/001-forum.xjs | 37 +++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/web/pages/.examples/001-forum.xjs b/web/pages/.examples/001-forum.xjs
index 5b008f1cb4..a0538536a1 100644
--- a/web/pages/.examples/001-forum.xjs
+++ b/web/pages/.examples/001-forum.xjs
@@ -23,8 +23,41 @@
         </li>
     </ol>
 
+    <?xjs
+        const offset = Req.has_param('offset') ? Req.get_param('offset') : 0;
+        const threads = forum_lib.listThreads(Req.get_param('sub'), offset, settings.page_size);
+    ?>
     <div id="forum-list-container" class="list-group">
-
+        <?xjs threads.forEach(function (e) { ?>
+            <a href="./?page<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>&thread=<? write(e.id); ?>" class="list-group-item striped">
+                <div class="row">
+                    <div class="col-sm-9">
+                        <strong><? write(e.subject); ?></strong>
+                        <p>By <strong><? write(e.first.from); ?></strong> on <? write(system.timestr(e.first.when_written_time)); ?></p>
+                        <p>Latest reply by <strong><? write(e.last.from); ?></strong> on <? write(system.timestr(e.last.when_written_time)); ?></p>
+                    </div>
+                    <div class="col-sm-3">
+                        <div class="pull-right">
+                            <?xjs if (e.votes.up || e.votes.down) { ?>
+                                <span id="uv-<? write(e.id); ?>" title="<? locale.write('badge_upvotes'); ?>" class="badge upvote-bg">
+                                    <span class="glyphicon glyphicon-arrow-up"></span>
+                                    <span id="uv-count-<? write(e.id); ?>"><? write(e.first.upvotes + ' / ' + e.votes.up); ?></span>
+                                </span>
+                                &nbsp;
+                                <span id="dv-<? write(e.id); ?>" title="<? locale.write('badge_downvotes'); ?>" class="badge downvote-bg">
+                                    <span class="glyphicon glyphicon-arrow-down"></span>
+                                    <span id="dv-count-<? write(e.id); ?>"><? write(e.first.downvotes + ' / ' + e.votes.down); ?></span>
+                                </span>
+                                &nbsp;
+                            <?xjs } ?>
+                            <?xjs if (auth_lib.is_user() && e.unread) { ?>
+                                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(e.scan_cfg&SCAN_CFG_NEW || e.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : ''); ?>" id="badge-<? write(e.id); ?>"><? write(e.unread); ?></span>
+                            <?xjs } ?>
+                        </div>
+                    </div>
+                </div>
+            </a>
+        <?xjs }); ?>
     </div>
 
 <?xjs } else if (Req.has_param('group')) { ?>
@@ -41,7 +74,7 @@
     <div id="forum-list-container" class="list-group">
         <?xjs const subs = forum_lib.listSubs(Req.get_param('group')); ?>
         <?xjs subs.forEach(function (e) { ?>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(e.index); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
                 <h4><strong><? write(e.name); ?></strong></h4>
                 <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(e.scan_cfg&SCAN_CFG_NEW || e.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : 'total'); ?>" id="badge-<? write(e.code); ?>"></span>
                 <p><? write(e.description); ?></p>
-- 
GitLab


From 460976f046670f60761b032210f4d1e478ba57f0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 20 Jul 2019 01:05:40 -0400
Subject: [PATCH 476/752] Whoops.

---
 web/root/api/forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 457eb66991..9741e63d89 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -6,7 +6,7 @@
 load('modopts.js');
 var settings = get_mod_options('web');
 
-load(settings.web_lib + 'init.js');
+load(settings.web_directory + '/lib/init.js');
 const auth_lib = load({}, settings.web_lib + 'auth.js');
 load(settings.web_lib + 'forum.js');
 
-- 
GitLab


From aec88496eb76658ee81d2fbb563972bdc6233b33 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 20 Jul 2019 01:52:11 -0400
Subject: [PATCH 477/752] Instead of deleting thread.messages, replace it with
 a message count (which I will probably display in the thread list).

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 66bc807790..31e9fc56c6 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -65,7 +65,7 @@ function listThreads(sub, offset, count) {
         var msgs = Object.keys(thread.messages);
         thread.first = thread.messages[msgs[0]];
         thread.last = thread.messages[msgs[msgs.length - 1]];
-        delete thread.messages;
+        thread.messages = msgs.length;
         ret.push(thread);
     }
 
-- 
GitLab


From 611940d748ef83cbad8eb338195aa0a3c5a3c442 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 20 Jul 2019 20:19:30 -0400
Subject: [PATCH 478/752] Loop until the heat death of the universe.

---
 web/root/api/events.ssjs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index bc73e9029d..16e336aee0 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -4,6 +4,8 @@ const settings = get_mod_options('web');
 load(settings.web_directory + '/lib/init.js');
 const auth_lib = load({}, settings.web_lib + 'auth.js');
 
+JSON.time_limit = 0;
+
 http_reply.header['Cache-Control'] = 'no-cache';
 http_reply.header['Content-type'] = 'text/event-stream';
 http_reply.header['X-Accel-Buffering'] = 'no'; // probably not needed by everyone (nginx)
-- 
GitLab


From b9424405c7513356ba59bb1ed4b6f54e31df5207 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 20 Jul 2019 20:20:58 -0400
Subject: [PATCH 479/752] Uh

---
 web/root/api/events.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 16e336aee0..648b359169 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -4,7 +4,7 @@ const settings = get_mod_options('web');
 load(settings.web_directory + '/lib/init.js');
 const auth_lib = load({}, settings.web_lib + 'auth.js');
 
-JSON.time_limit = 0;
+js.time_limit = 0;
 
 http_reply.header['Cache-Control'] = 'no-cache';
 http_reply.header['Content-type'] = 'text/event-stream';
-- 
GitLab


From e27d3ebf3e86fbbba07ae5a630c0c6c5d0f7930b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 21 Jul 2019 18:07:57 -0400
Subject: [PATCH 480/752] Include total count of threads when returning a
 subset.

---
 web/lib/forum.js        | 17 +++++++++++++++--
 web/root/api/forum.ssjs |  2 +-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 31e9fc56c6..f41c74417d 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -59,14 +59,14 @@ function listThreads(sub, offset, count) {
     if (offset >= threads.order.length) return false;
 
     var stop = Math.min(threads.order.length, offset + count);
-    var ret = [];
+    var ret = { total: threads.order.length, threads : [] };
     for (var n = offset; n < stop; n++) {
         var thread = threads.thread[threads.order[n]];
         var msgs = Object.keys(thread.messages);
         thread.first = thread.messages[msgs[0]];
         thread.last = thread.messages[msgs[msgs.length - 1]];
         thread.messages = msgs.length;
-        ret.push(thread);
+        ret.threads.push(thread);
     }
 
     return ret;
@@ -1085,4 +1085,17 @@ function getMessageThreads(sub, max) {
 
 }
 
+function isValidRequest() {
+    if (Req.has_param('group')) {
+        const grp = Req.get_param('group');
+        if (typeof msg_area.grp_list[grp] == 'undefined') return false;
+        if (!user.compare_ars(msg_area.grp_list[grp].ars)) return false;
+    }
+    if (Req.has_param('sub')) {
+        const sub = Req.get_param('sub');
+        if (typeof msg_area.sub[sub] == 'undefined') return false;
+    }
+    return true;
+}
+
 this;
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 9741e63d89..d3ef5b4ef5 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -218,7 +218,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                         http_request.query.sub[0],
                         http_request.query.offset[0],
                         count || settings.page_size
-                    );
+                    ).threads;
                 }
                 break;
 
-- 
GitLab


From 34be68fe8119150d9479698b9ab7a42b65e2793d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 21 Jul 2019 18:09:19 -0400
Subject: [PATCH 481/752] writef method for formatted strings, although I hear
 these can be very perplexing and require lots of documentation.

---
 web/lib/locale/en_us.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/web/lib/locale/en_us.js b/web/lib/locale/en_us.js
index 64cc8a52c8..7a8c8dd2c1 100644
--- a/web/lib/locale/en_us.js
+++ b/web/lib/locale/en_us.js
@@ -67,4 +67,9 @@ EN_US.prototype.write = function (str, sec) {
     write(this.strings[sec || this.section][str]);
 }
 
+EN_US.prototype.writef = function (str) {
+    const args = Array.concat(this.strings[this.section][str], Array.apply(null, arguments).slice(1));
+    write(format.apply(null, args));
+}
+
 var Locale = EN_US;
-- 
GitLab


From b958dbb51ba6a85b3c31682458ca6843e589dd66 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 21 Jul 2019 18:10:22 -0400
Subject: [PATCH 482/752] Some more strings for the XJS forum.

---
 web/lib/locale/en_us.ini | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/web/lib/locale/en_us.ini b/web/lib/locale/en_us.ini
index 6db39b965a..66ca16e6c6 100644
--- a/web/lib/locale/en_us.ini
+++ b/web/lib/locale/en_us.ini
@@ -72,7 +72,9 @@ label_message_from = From
 label_message_to = To
 label_message_date = on
 label_message_subject = Subject
-label_thread_latest_reply = Latest reply by
+label_thread_latest_reply = Latest reply from
+suffix_reply_count = reply
+suffix_replies_count = replies
 badge_poll = POLL
 badge_downvotes = Downvotes - Parent / Thread Total
 badge_upvotes = Upvotes - Parent / Thread Total
@@ -82,8 +84,12 @@ button_post_poll = Post a new poll
 button_scan_new = Include in new message scan
 button_scan_you = Include in new message scan (messages to you only)
 button_scan_off = Exclude from new message scan
-button_load_more = Load more
-message_loading_threads = Loading threads ...
+button_thread_first_page = First page
+button_thread_back_pages = Jump %s page(s) backward
+button_thread_previous_page = Previous page
+button_thread_next_page = Next page
+button_thread_forward_pages = Jump %s page(s) forward
+button_thread_last_page = Last page
 
 [sidebar_node_list]
 label_title = Who's Online
-- 
GitLab


From 11bd7a6194d26ee12cac5e448f61fdad78ba175f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 21 Jul 2019 18:11:06 -0400
Subject: [PATCH 483/752] Some enhancements. Thread listing view nearing
 completion. This remains hidden, unusable, and a work in progress.

---
 web/pages/.examples/001-forum.xjs | 158 ++++++++++++++++++++++++++----
 1 file changed, 141 insertions(+), 17 deletions(-)

diff --git a/web/pages/.examples/001-forum.xjs b/web/pages/.examples/001-forum.xjs
index a0538536a1..d9160cab73 100644
--- a/web/pages/.examples/001-forum.xjs
+++ b/web/pages/.examples/001-forum.xjs
@@ -7,37 +7,123 @@
     locale.section = 'page_forum';
 ?>
 
+<!-- To do: Search (forum, group, sub, or thread) -->
+
 <?xjs if (Req.has_param('sub') && Req.has_param('thread')) { ?>
 
-<?xjs } else if (Req.has_param('sub')) { ?>
+    <ol class="breadcrumb">
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>">
+                <? locale.write('title'); ?>
+            </a>
+        </li>
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>">
+                <? write(msg_area.grp_list[msg_area.sub[Req.get_param('sub')].grp_index].name); ?>
+            </a>
+        </li>
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>">
+                <? write(msg_area.sub[Req.get_param('sub')].name); ?>
+            </a>
+        </li>
+        <li>
+            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>&thread=<? Req.write_param('thread'); ?>">
+                Thread <? Req.write_param('thread'); ?>
+            </a>
+        </li>
+    </ol>
+
+    <?xjs
+        var offset = Req.has_param('offset') ? parseInt(Req.get_param('offset')) : 0;
+        const thread = forum_lib.getMessageThreads(Req.get_param('sub'), settings.max_messages);
+    ?>
+
+    <div id="jump-unread-container" style="margin-bottom:1em;">
+        <a class="btn btn-default" id="jump-unread" title="Jump to first unread message" href="#">
+            <span class="glyphicon glyphicon-star"></span>
+        </a>
+    </div>
+
+    <div id="forum-list-container" class="list-group">
+
+    </div>
+
+<?xjs } else if (Req.has_param('sub') && typeof msg_area.sub[Req.get_param('sub')] != 'undefined') { ?>
 
     <ol class="breadcrumb">
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+            <a href="./?page=<? Req.write_param('page'); ?>">
+                <? locale.write('title'); ?>
+            </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>"><? write(msg_area.grp_list[Req.get_param('group')].name); ?></a>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(msg_area.grp_list[msg_area.sub[Req.get_param('sub')].grp_index].number); ?>">
+                <? write(msg_area.grp_list[msg_area.sub[Req.get_param('sub')].grp_index].name); ?>
+            </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>&sub=<? Req.write_param('sub'); ?>"><? write(msg_area.sub[Req.get_param('sub')].name); ?></a>
+            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>">
+                <? write(msg_area.sub[Req.get_param('sub')].name); ?>
+            </a>
         </li>
     </ol>
 
     <?xjs
-        const offset = Req.has_param('offset') ? Req.get_param('offset') : 0;
+        var offset = Req.has_param('offset') ? parseInt(Req.get_param('offset')) : 0;
         const threads = forum_lib.listThreads(Req.get_param('sub'), offset, settings.page_size);
+        const sub = msg_area.sub[Req.get_param('sub')];
     ?>
+
+    <button class="btn btn-default icon" aria-label="<? locale.write('button_post_message'); ?>" title="<? locale.write('button_post_message'); ?>" onclick="addNew('<? write(sub.code); ?>')">
+        <span class="glyphicon glyphicon-pencil"></span>
+    </button>
+    <button class="btn btn-default icon" aria-label="<? locale.write('button_post_poll'); ?>" title="<? locale.write('button_post_poll'); ?>" onclick="addPoll('<? write(sub.code); ?>')">
+        <span class="glyphicon glyphicon-list-alt"></span>
+    </button>
+
+    <div class="pull-right">
+        <button id="scan-cfg-new" class="btn btn-default icon" aria-label="<? locale.write('button_scan_new'); ?>" title="<? locale.write('button_scan_new'); ?>" onclick="setScanCfg('<? write(sub.code); ?>', 1)">
+            <span class="glyphicon glyphicon-ok-sign"></span>
+        </button>
+        <button id="scan-cfg-youonly" class="btn btn-default icon" aria-label="<? locale.write('button_scan_you'); ?>" title="<? locale.write('button_scan_you'); ?>" onclick="setScanCfg('<? write(sub.code); ?>', 2)">
+            <span class="glyphicon glyphicon-user"></span>
+        </button>
+        <button id="scan-cfg-off" class="btn btn-primary icon" aria-label="<? locale.write('button_scan_off'); ?>" title="<? locale.write('button_scan_off'); ?>" onclick="setScanCfg('<? write(sub.code); ?>', 0)">
+            <span class="glyphicon glyphicon-ban-circle"></span>
+        </button>
+    </div>
+
     <div id="forum-list-container" class="list-group">
-        <?xjs threads.forEach(function (e) { ?>
-            <a href="./?page<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>&thread=<? write(e.id); ?>" class="list-group-item striped">
+        <?xjs threads.threads.forEach(function (e) { ?>
+            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>&thread=<? write(e.id); ?>" class="list-group-item striped">
                 <div class="row">
                     <div class="col-sm-9">
                         <strong><? write(e.subject); ?></strong>
-                        <p>By <strong><? write(e.first.from); ?></strong> on <? write(system.timestr(e.first.when_written_time)); ?></p>
-                        <p>Latest reply by <strong><? write(e.last.from); ?></strong> on <? write(system.timestr(e.last.when_written_time)); ?></p>
+                        <p><strong><? write(e.first.from); ?></strong>, <? write(system.timestr(e.first.when_written_time)); ?></p>
+                        <?xjs if (e.messages > 1) { ?>
+                            <p>
+                                <strong><? write(e.messages - 1); ?></strong>
+                                <?xjs
+                                    if (e.messages == 2) {
+                                        locale.write('suffix_reply_count');
+                                    } else {
+                                        locale.write('suffix_replies_count');
+                                    }
+                                ?>,
+                                <? locale.write('label_thread_latest_reply'); ?>
+                                <strong><? write(e.last.from); ?></strong>,
+                                <? write(system.timestr(e.last.when_written_time)); ?>
+                            </p>
+                        <?xjs } ?>
                     </div>
                     <div class="col-sm-3">
                         <div class="pull-right">
+                            <?xjs if (auth_lib.is_user() && e.unread) { ?>
+                                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : ''); ?>" id="badge-<? write(e.id); ?>">
+                                    <? write(e.unread); ?>
+                                </span>
+                            <?xjs } ?>
                             <?xjs if (e.votes.up || e.votes.down) { ?>
                                 <span id="uv-<? write(e.id); ?>" title="<? locale.write('badge_upvotes'); ?>" class="badge upvote-bg">
                                     <span class="glyphicon glyphicon-arrow-up"></span>
@@ -48,10 +134,6 @@
                                     <span class="glyphicon glyphicon-arrow-down"></span>
                                     <span id="dv-count-<? write(e.id); ?>"><? write(e.first.downvotes + ' / ' + e.votes.down); ?></span>
                                 </span>
-                                &nbsp;
-                            <?xjs } ?>
-                            <?xjs if (auth_lib.is_user() && e.unread) { ?>
-                                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(e.scan_cfg&SCAN_CFG_NEW || e.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : ''); ?>" id="badge-<? write(e.id); ?>"><? write(e.unread); ?></span>
                             <?xjs } ?>
                         </div>
                     </div>
@@ -60,21 +142,63 @@
         <?xjs }); ?>
     </div>
 
-<?xjs } else if (Req.has_param('group')) { ?>
+    <?xjs if (threads.total / settings.page_size > 1) { ?>
+        <?xjs qs = http_request.query_string.replace(/&offset=\d+/g, ''); ?>
+        <div class="btn-group">
+            <?xjs if (offset > 0) { ?>
+                <a href="./?<? write(qs); ?>&offset=0" role="button" class="btn btn-default" title="<? locale.write('button_thread_first_page'); ?>">
+                    <span class="glyphicon glyphicon-fast-backward"></span>
+                </a>
+                <a href="./?<? write(qs); ?>&offset=<? write(Math.max(0, offset - 9)); ?>" role="button" class="btn btn-default" title="<? locale.writef('button_thread_back_pages', Math.min(9, offset)); ?>">
+                    <span class="glyphicon glyphicon-backward"></span>
+                </a>
+                <a href="./?<? write(qs); ?>&offset=<? write(Math.max(0, offset - 1)); ?>" role="button" class="btn btn-default" title="<? locale.write('button_thread_previous_page'); ?>">
+                    <span class="glyphicon glyphicon-step-backward"></span>
+                </a>
+            <?xjs } ?>
+            <a href="./?<? write(qs); ?>&offset=<? write(offset); ?>" role="button" class="btn btn-default" disabled>
+                <? write(offset + 1); ?>
+            </a>
+            <?xjs for (var n = offset + 1; n < threads.total / settings.page_size && n - offset < 9; n++) { ?>
+                <a href="./?<? write(qs); ?>&offset=<? write(n); ?>" role="button" class="btn btn-default"><? write(n + 1); ?></a>
+            <?xjs } ?>
+            <?xjs if (threads.total / settings.page_size > offset + 9) { ?>
+                <a href="./?<? write(qs); ?>&offset=<? write(offset + 1); ?>" role="button" class="btn btn-default" title="<? locale.write('button_thread_next_page'); ?>">
+                    <span class="glyphicon glyphicon-step-forward"></span>
+                </a>
+                <a href="./?<? write(qs); ?>&offset=<? write(offset + 9); ?>" role="button" class="btn btn-default" title="<? locale.writef('button_thread_forward_pages', Math.min(9, Math.floor(threads.total / settings.page_size))); ?>">
+                    <span class="glyphicon glyphicon-forward"></span>
+                </a>
+                <a href="./?<? write(qs); ?>&offset=<? write(Math.floor(threads.total / settings.page_size)); ?>" role="button" class="btn btn-default" title="<? locale.write('button_thread_last_page'); ?>">
+                    <span class="glyphicon glyphicon-fast-forward"></span>
+                </a>
+            <?xjs } ?>
+        </div class="btn-group">
+    <?xjs } ?>
+
+    <?xjs if (auth_lib.is_user()) { ?>
+        <!-- To do: Subscribe to vote, unread events -->
+    <?xjs } ?>
+
+<?xjs } else if (Req.has_param('group') && typeof msg_area.grp_list[Req.get_param('group')] != 'undefined') { ?>
 
     <ol class="breadcrumb">
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+            <a href="./?page=<? Req.write_param('page'); ?>">
+                <? locale.write('title'); ?>
+            </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>"><? write(msg_area.grp_list[Req.get_param('group')].name); ?></a>
+            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>">
+                <? write(msg_area.grp_list[Req.get_param('group')].name); ?>
+            </a>
         </li>
     </ol>
 
     <div id="forum-list-container" class="list-group">
         <?xjs const subs = forum_lib.listSubs(Req.get_param('group')); ?>
         <?xjs subs.forEach(function (e) { ?>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
+            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
                 <h4><strong><? write(e.name); ?></strong></h4>
                 <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(e.scan_cfg&SCAN_CFG_NEW || e.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : 'total'); ?>" id="badge-<? write(e.code); ?>"></span>
                 <p><? write(e.description); ?></p>
-- 
GitLab


From c296cd835a648f9bd28a8c42065a63ee48dab996 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 21 Jul 2019 19:48:24 -0400
Subject: [PATCH 484/752] get_all_msg_headers may return undefined, so deal
 with that.

---
 web/lib/forum.js | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index f41c74417d..b9ea8e8c92 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -957,14 +957,13 @@ function getMessageThreads(sub, max) {
 
     var msgBase = new MsgBase(sub);
     if (!msgBase.open()) return threads;
-    if ((typeof max === 'number' && max > 0) ||
-        typeof msgBase.get_all_msg_headers !== 'function'
-    ) {
+    if ((typeof max == 'number' && max > 0) || typeof msgBase.get_all_msg_headers != 'function') {
         var headers = getSomeMessageHeaders(msgBase, max);
     } else {
         var headers = msgBase.get_all_msg_headers();
     }
     msgBase.close();
+    if (!headers) return threads;
 
     Object.keys(headers).forEach(
 
@@ -1075,11 +1074,9 @@ function getMessageThreads(sub, max) {
 
     );
 
-    threads.order = Object.keys(threads.thread).sort(
-        function (a, b) {
-            return threads.thread[b].newest - threads.thread[a].newest;
-        }
-    );
+    threads.order = Object.keys(threads.thread).sort(function (a, b) {
+        return threads.thread[b].newest - threads.thread[a].newest;
+    });
 
     return threads;
 
-- 
GitLab


From df41c782dadb01681ff02c813a7d731d8ba74190 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sun, 21 Jul 2019 19:48:42 -0400
Subject: [PATCH 485/752] Breadcrumb <ol> overflow.

---
 web/root/css/style.css | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 6e8cd22b37..16281fe0bc 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -161,3 +161,13 @@ animation: indicator-fade 3s ease 0s 1 alternate !important;
     left: 100%;
     margin-top: -6px;
 } 
+
+.breadcrumb {
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.breadcrumb li {
+	display: inline;
+}
\ No newline at end of file
-- 
GitLab


From 09fc10a02960b27739ca0ef9ed22447cdaeef82b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 24 Jul 2019 10:13:53 -0400
Subject: [PATCH 486/752] Include is_utf8 header property in message thread
 headers. Some new methods for use in the revised forum.

---
 web/lib/forum.js | 110 +++++++++++++++++++++++++++++++----------------
 1 file changed, 74 insertions(+), 36 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index b9ea8e8c92..2fa1a2af2a 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -16,45 +16,42 @@ function listGroups() {
 
 // Returns an array of objects of "useful" information about subs
 function listSubs(group) {
-    var response = [];
-    msg_area.grp_list[group].sub_list.forEach(
-        function (sub) {
-            response.push(
-                {   index : sub.index,
-                    code : sub.code,
-                    grp_index : sub.grp_index,
-                    grp_name : sub.grp_name,
-                    name : sub.name,
-                    description : sub.description,
-                    qwk_name : sub.qwk_name,
-                    qwk_conf : sub.qwk_conf,
-                    qwk_tagline : sub.qwknet_tagline,
-                    newsgroup : sub.newsgroup,
-                    ars : sub.ars,
-                    read_ars : sub.read_ars,
-                    can_read : sub.can_read,
-                    post_ars : sub.post_ars,
-                    can_post : sub.can_post,
-                    operator_ars : sub.operator_ars,
-                    is_operator : sub.is_operator,
-                    moderated_ars : sub.moderated_ars,
-                    is_moderated : sub.is_moderated,
-                    scan_ptr : sub.scan_ptr,
-                    scan_cfg : sub.scan_cfg
-                }
-            );
-        }
-    );
-    return response;
+    return msg_area.grp_list[group].sub_list.map(function (sub) {
+        return {
+            index: sub.index,
+            code: sub.code,
+            grp_index: sub.grp_index,
+            grp_name: sub.grp_name,
+            name: sub.name,
+            description: sub.description,
+            qwk_name: sub.qwk_name,
+            qwk_conf: sub.qwk_conf,
+            qwk_tagline: sub.qwknet_tagline,
+            newsgroup: sub.newsgroup,
+            ars: sub.ars,
+            read_ars: sub.read_ars,
+            can_read: sub.can_read,
+            post_ars: sub.post_ars,
+            can_post: sub.can_post,
+            operator_ars: sub.operator_ars,
+            is_operator: sub.is_operator,
+            moderated_ars: sub.moderated_ars,
+            is_moderated: sub.is_moderated,
+            scan_ptr: sub.scan_ptr,
+            scan_cfg: sub.scan_cfg
+        };
+    });
 }
 
-function listThreads(sub, offset, count) {
+function listThreads(sub, offset, count, page_offset) {
 
     offset = parseInt(offset);
     if (isNaN(offset) || offset < 0) return false;
     count = parseInt(count);
     if (isNaN(count) || count < 1) return false;
 
+    if (page_offset) offset = offset * count;
+
     var threads = getMessageThreads(sub, settings.max_messages);
     if (offset >= threads.order.length) return false;
 
@@ -131,19 +128,59 @@ function getGroupUnreadCounts() {
     }, {});
 }
 
-function getUnreadInThread(sub, thread) {
-    if (typeof thread === 'number') {
+function getUnreadInThread(sub, thread, mkeys) {
+    if (typeof thread == 'number') {
         var threads = getMessageThreads(sub, settings.max_messages);
-        if (typeof threads.thread[thread] === 'undefined') return 0;
+        if (typeof threads.thread[thread] == 'undefined') return 0;
         thread = threads.thread[thread];
     }
     var count = 0;
-    Object.keys(thread.messages).forEach(function (m) {
+    if (!mkeys) mkeys = Object.keys(thread.messages);
+    mkeys.forEach(function (m) {
         if (thread.messages[m].number > msg_area.sub[sub].scan_ptr) count++;
     });
     return count;
 }
 
+function getThreadVoteTotals(thread, mkeys) {
+    return mkeys.reduce(function (a, c, i) {
+        if (thread.messages[c].upvotes > 0) {
+            if (i == 0) a.up.p++;
+            a.up.t++;
+        }
+        if (thread.messages[c].downvotes > 0) {
+            if (i == 0) a.down.p++;
+            a.down.t++;
+        }
+        a.total = a.up.t + a.down.t;
+        return a;
+    }, { up: { p: 0, t: 0 }, down: { p: 0, t: 0 }, total: 0 });
+}
+
+// { [thread_id]: { total, unread, votes: { up: { parent, total }, down: { parent, total }, total }, newest } }
+function getThreadStats(sub, offset, page_size) {
+    const threads = getMessageThreads(sub, settings.max_messages);
+    const ret = {};
+    if (!offset) offset = 0;
+    if (!page_size) page_size = threads.order.length - offset;
+    offset = offset * page_size;
+    var stop = Math.min(threads.order.length, offset + page_size);
+    for (var n = offset; n < stop; n++) {
+        var thread = threads.thread[threads.order[n]];
+        var mkeys = Object.keys(thread.messages);
+        ret[threads.order[n]] = {
+            total: mkeys.length,
+            unread: getUnreadInThread(sub, thread, mkeys),
+            votes: getThreadVoteTotals(thread, mkeys),
+            newest: {
+                from: thread.messages[mkeys[mkeys.length - 1]].from,
+                date: system.timestr(thread.messages[mkeys[mkeys.length - 1]].when_written_time)
+            }
+        };
+    }
+    return ret;
+}
+
 function getVotesInThread(sub, thread) {
     var ret = { t : { u : 0, d : 0 }, m : {} };
     if (typeof msg_area.sub[sub] === 'undefined') return ret;
@@ -905,7 +942,8 @@ function getMessageThreads(sub, max) {
             to : header.to,
             when_written_time : header.when_written_time,
             upvotes : (header.attr&MSG_POLL ? 0 : (header.upvotes || 0)),
-            downvotes : (header.attr&MSG_POLL ? 0 : (header.downvotes || 0))
+            downvotes : (header.attr&MSG_POLL ? 0 : (header.downvotes || 0)),
+            is_utf8: header.is_utf8
         };
         if (header.attr&MSG_POLL) {
             header.field_list.sort(
-- 
GitLab


From 4a752a3805485603962b71e840a9afc93ca3d3fd Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 24 Jul 2019 10:15:03 -0400
Subject: [PATCH 487/752] Event for pushing thread stats.

---
 web/lib/events/forum.js | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/web/lib/events/forum.js b/web/lib/events/forum.js
index a237062e35..f474bbba2c 100644
--- a/web/lib/events/forum.js
+++ b/web/lib/events/forum.js
@@ -2,6 +2,7 @@ const forum_lib = load({}, settings.web_lib + 'forum.js');
 
 var last_subs;
 var last_groups;
+var last_threads;
 var last_run = 0;
 const frequency = (settings.refresh_interval || 60000) / 1000;
 
@@ -26,7 +27,6 @@ function scan_groups() {
     const scan = forum_lib.getGroupUnreadCounts();
     if (!last_groups) {
         forum_emit('groups_unread', scan);
-        emit({ event: 'forum', data: JSON.stringify(scan) });
     } else {
         const diff = shallow_diff(last_groups, scan);
         if (diff) forum_emit('groups_unread', scan);
@@ -34,8 +34,8 @@ function scan_groups() {
     last_groups = scan;
 }
 
-function scan_subs() {
-    const scan = forum_lib.getSubUnreadCounts(Req.get_param('subs_unread'));
+function scan_subs(group) {
+    const scan = forum_lib.getSubUnreadCounts(group);
     if (!last_subs) {
         forum_emit('subs_unread', scan);
     } else {
@@ -45,12 +45,33 @@ function scan_subs() {
     last_subs = scan;
 }
 
+function scan_threads(sub, offset, page_size) {
+    const scan = forum_lib.getThreadStats(sub, offset, page_size);
+    if (!last_threads) {
+        forum_emit('threads', scan);
+    } else {
+        const ret = Object.keys(scan).reduce(function (a, c) {
+            if (typeof last_threads[c] == 'undefined') {
+                a[c] = scan[c];
+            } else if (scan[c].total != last_threads[c].total) {
+                a[c] = scan[c];
+            } else if (scan[c].votes.total != last_threads[c].votes.total) {
+                a[c] = scan[c];
+            }
+            return a;
+        }, {});
+        if (Object.keys(ret).length) forum_emit('threads', ret);
+    }
+    last_threads = scan;
+}
+
 function cycle() {
     if (!auth_lib.is_user()) return;
     if (time() - last_run <= frequency) return;
     last_run = time();
     if (Req.has_param('groups_unread')) scan_groups();
-    if (Req.has_param('subs_unread')) scan_subs();
+    if (Req.has_param('subs_unread')) scan_subs(Req.get_param('subs_unread'));
+    if (Req.has_param('threads')) scan_threads(Req.get_param('threads'), Req.get_param('offset'), Req.get_param('page_size'));
 }
 
 this;
-- 
GitLab


From 4c5795afa451c66b1fa452c3ee5a91f49c342e66 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 24 Jul 2019 10:15:48 -0400
Subject: [PATCH 488/752] Handler for thread stats event

---
 web/root/js/forum.js | 55 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 025fb407cc..35ce37e738 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -316,6 +316,61 @@ function getGroupUnreadCount(group) {
 	$.getJSON('./api/forum.ssjs?call=get-group-unread-count&group=' + group, onGroupUnreadCount);
 }
 
+function onThreadStats(data) {
+	Object.keys(data).forEach(e => {
+		
+		let div1;
+		if (!$('#replies-' + e).length) {
+			div1 = $('#forum-thread-replies-template').clone();
+			div1.attr('id', 'replies-' + e);
+			$('#left-' + e).append(div1);
+		} else {
+			div1 = $('#replies-' + e);
+		}
+		if (data[e].total > 1) {
+			div1.find('strong[data-message-count]').first().html(data[e].total - 1);
+			if (data[e].total == 2) {
+				div1.find('span[data-suffix-reply]').first().attr('hidden', false);
+			} else {
+				div1.find('span[data-suffix-replies]').first().attr('hidden', false);
+			}
+			div1.find('strong[data-last-from]').first().html(data[e].newest.from);
+			div1.find('span[data-last-time]').first().html(data[e].newest.date);
+			div1.attr('hidden', false);
+		}
+
+		let div2;
+		if (!$('#stats-' + e).length) {
+			div2 = $('#forum-thread-stats-template').clone();
+			div2.attr('id', 'stats-' + e);
+			$('#right-' + e).append(div2);
+		} else {
+			div2 = $('#stats-' + e);
+		}
+		if (data[e].unread) {
+			const urm = div2.find('span[data-unread-messages]');
+			if (urm.length) {
+				urm.first().html(data[e].unread);
+				urm.first().attr('hidden', false);
+				div2.attr('hidden', false);
+			}
+		}
+		
+		if (data[e].votes.total) {
+			if (data[e].votes.up.t) {
+				div2.find('span[data-upvotes]').first().html(data[e].votes.up.p + '/' + data[e].votes.up.t);
+				div2.find('span[data-upvotes-badge]').first().css('display', '');
+			}
+			if (data[e].votes.down.t) {
+				div2.find('span[data-downvotes]').first().html(data[e].votes.down.p + '/' + data[e].votes.down.t);
+				div2.find('span[data-downvotes-badge]').first().css('display', '');
+			}
+			div2.attr('hidden', false);
+		}
+		
+	});
+}
+
 /*  Fetch a private mail message's body (with links to attachments) where 'id'
 	is the message number.	Output it to an element with id 'message-<id>'. */
 function getMailBody(id) {
-- 
GitLab


From 155af6bc9a5feac50d280b2a10a6383c08e486e2 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 24 Jul 2019 13:35:26 -0700
Subject: [PATCH 489/752] Don't html-entity-encode UTF-8 message body text.

As a side-note: when forum_extended_ascii is fale, we should probably
be converting the text to ASCII, not just sending it as-is to the
browser which won't be displaying the potentially CP437 chars
correctly.
---
 web/pages/.examples/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/pages/.examples/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
index c66b57f7d5..45961183bc 100644
--- a/web/pages/.examples/001-forum.ssjs
+++ b/web/pages/.examples/001-forum.ssjs
@@ -333,7 +333,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		// This is a normal message
 		} else {
-			writeln(formatMessage(body, false, settings.forum_extended_ascii));
+			writeln(formatMessage(body, false, settings.forum_extended_ascii && !header.is_utf8));
 			writeln(strings.message.body.close);
 		}
 
-- 
GitLab


From c30c7e7a499b1a6b9639059c8dfb41233677b37a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:02:25 -0400
Subject: [PATCH 490/752] Some require & load changes. Much of this script is
 garbage, but I'll clean it up after the new forum is closer to done.

---
 web/root/api/forum.ssjs | 43 ++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 22 deletions(-)

diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index d3ef5b4ef5..e6e8a7939e 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -3,12 +3,11 @@
     is done here; otherwise all permission checking is done at the function
     level. */
 
-load('modopts.js');
-var settings = get_mod_options('web');
+var settings = load('modopts.js', 'web');
 
-load(settings.web_directory + '/lib/init.js');
+require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
 const auth_lib = load({}, settings.web_lib + 'auth.js');
-load(settings.web_lib + 'forum.js');
+const forum_lib = load({}, settings.web_lib + 'forum.js');
 
 var reply = {};
 
@@ -32,12 +31,12 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 
             case 'get-mail-body':
                 if (typeof http_request.query.number !== 'undefined') {
-                    reply = getMailBody(http_request.query.number[0]);
+                    reply = forum_lib.getMailBody(http_request.query.number[0]);
                 }
                 break;
 
             case 'get-signature':
-                reply.signature = getSignature();
+                reply.signature = forum_lib.getSignature();
                 break;
 
             case 'post-reply':
@@ -45,7 +44,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     typeof http_request.query.body !== 'undefined' &&
                     typeof http_request.query.pid !== 'undefined'
                 ) {
-                    reply.success = postReply(
+                    reply.success = forum_lib.postReply(
                         http_request.query.sub[0],
                         http_request.query.body[0],
                         Number(http_request.query.pid[0])
@@ -61,7 +60,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     typeof http_request.query.subject !== 'undefined' &&
                     typeof http_request.query.body !== 'undefined'
                 ) {
-                    reply.success = postNew(
+                    reply.success = forum_lib.postNew(
                         http_request.query.sub[0],
                         http_request.query.to[0],
                         http_request.query.subject[0],
@@ -76,7 +75,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.number !== 'undefined'
                 ) {
-                    reply.success = deleteMessage(
+                    reply.success = forum_lib.deleteMessage(
                         http_request.query.sub[0],
                         http_request.query.number[0]
                     );
@@ -87,7 +86,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 
             case 'delete-mail':
                 if (typeof http_request.query.number !== 'undefined') {
-                    reply.success = deleteMail(http_request.query.number);
+                    reply.success = forum_lib.deleteMail(http_request.query.number);
                 } else {
                     reply.success = false;
                 }
@@ -97,7 +96,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.cfg !== 'undefined'
                 ) {
-                    reply.success = setScanCfg(
+                    reply.success = forum_lib.setScanCfg(
                         http_request.query.sub[0],
                         http_request.query.cfg[0]
                     );
@@ -112,7 +111,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     typeof http_request.query.up !== 'undefined' &&
                     !(user.security.restrictions&UFLAG_V)
                 ) {
-                    reply.success = voteMessage(
+                    reply.success = forum_lib.voteMessage(
                         http_request.query.sub[0],
                         http_request.query.id[0],
                         http_request.query.up[0]
@@ -127,7 +126,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     typeof http_request.query.id !== 'undefined' &&
                     typeof http_request.query.answer !== 'undefined'
                 ) {
-                    reply.success = submitPollAnswers(
+                    reply.success = forum_lib.submitPollAnswers(
                         http_request.query.sub[0],
                         http_request.query.id[0],
                         http_request.query.answer
@@ -142,7 +141,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     typeof http_request.query.results !== 'undefined' &&
                     typeof http_request.query.answer !== 'undefined'
                 ) {
-                    reply.success = postPoll(
+                    reply.success = forum_lib.postPoll(
                         http_request.query.sub[0],
                         http_request.query.subject[0],
                         http_request.query.votes[0],
@@ -172,7 +171,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 ) {
                     var id = parseInt(http_request.query.id[0]);
                     if (!isNaN(id)) {
-                        reply = getVotesInThread(
+                        reply = forum_lib.getVotesInThread(
                             http_request.query.sub[0],
                             id
                         );
@@ -182,7 +181,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 
             case 'get-sub-votes':
                 if (typeof http_request.query.sub !== 'undefined') {
-                    reply = getVotesInThreads(http_request.query.sub[0]);
+                    reply = forum_lib.getVotesInThreads(http_request.query.sub[0]);
                 }
                 break;
 
@@ -190,7 +189,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 if (typeof http_request.query.sub !== 'undefined' &&
                     typeof http_request.query.id !== 'undefined'
                 ) {
-                    reply = getUserPollData(
+                    reply = forum_lib.getUserPollData(
                         http_request.query.sub[0],
                         http_request.query.id[0]
                     );
@@ -198,12 +197,12 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 break;
 
             case 'list-groups':
-                reply = listGroups();
+                reply = forum_lib.listGroups();
                 break;
 
             case 'list-subs':
                 if (typeof http_request.query.group !== 'undefined') {
-                    reply = listSubs(http_request.query.group[0]);
+                    reply = forum_lib.listSubs(http_request.query.group[0]);
                 }
                 break;
 
@@ -214,7 +213,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                     if (typeof http_request.query.count !== 'undefined') {
                         var count = http_request.query.count[0];
                     }
-                    reply = listThreads(
+                    reply = forum_lib.listThreads(
                         http_request.query.sub[0],
                         http_request.query.offset[0],
                         count || settings.page_size
@@ -226,7 +225,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 if (typeof http_request.query.group !== 'undefined') {
                     http_request.query.group.forEach(
                         function(group) {
-                            reply[group] = getGroupUnreadCount(group);
+                            reply[group] = forum_lib.getGroupUnreadCount(group);
                         }
                     );
                 }
@@ -236,7 +235,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
                 if (typeof http_request.query.sub !== 'undefined') {
                     http_request.query.sub.forEach(
                         function(sub) {
-                            reply[sub] = getSubUnreadCount(sub);
+                            reply[sub] = forum_lib.getSubUnreadCount(sub);
                         }
                     );
                 }
-- 
GitLab


From 2792412689f4e369f990562581a514609066bdd7 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:05:33 -0400
Subject: [PATCH 491/752] Some require & load changes. Load SSJS and XJS
 pages/sidebar modules in an IIFE closure.

---
 web/lib/pages.js   | 12 ++++++++++--
 web/lib/sidebar.js | 10 ++++++++--
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index 74925577e5..bca8086c88 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -1,3 +1,5 @@
+require('xjs.js', 'xjs_compile');
+
 function getWebCtrl(dir) {
 	if (!file_exists(dir + 'webctrl.ini')) return false;
 	var f = new File(dir + 'webctrl.ini');
@@ -150,10 +152,14 @@ function getPage(page) {
 	switch(ext) {
 		case '.SSJS':
 			if (ext === '.SSJS' && page.search(/\.xjs\.ssjs$/i) >= 0) break;
-			load({}, page, true);
+			(function () {
+				load({}, page, true);
+			})();
 			break;
 		case '.XJS':
-			load({}, xjs_compile(page), true);
+			(function () {
+				load({}, xjs_compile(page), true);
+			})();
 			break;
 		case '.HTML':
 			var f = new File(page);
@@ -185,3 +191,5 @@ function writePage(page) {
 		write(getPage(page));
 	}
 }
+
+this;
\ No newline at end of file
diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index 0135dba83f..3de128415e 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -23,10 +23,14 @@ function getSidebarModule(module) {
 	switch (ext) {
 		case '.SSJS':
 			if (ext === '.SSJS' && module.search(/\.xjs\.ssjs$/i) >= 0) break;
-			load({}, module, true);
+			(function () {
+				load({}, module, true);
+			})();
 			break;
 		case '.XJS':
-			load({}, xjs_compile(module), true);
+			(function () {
+				load({}, xjs_compile(module), true);
+			})();
 			break;
 		case '.HTML':
 			ret = getFileContents(module);
@@ -56,3 +60,5 @@ function writeSidebarModules() {
 	);
 	write('</ul>');
 }
+
+this;
\ No newline at end of file
-- 
GitLab


From 3d2c551339c0a59c8854170da03ebb0b12ff2d70 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:07:31 -0400
Subject: [PATCH 492/752] Remove load()s that are no longer (or were never?)
 used.

---
 web/lib/events/mail.js     | 2 --
 web/lib/events/sbbsimsg.js | 2 --
 2 files changed, 4 deletions(-)

diff --git a/web/lib/events/mail.js b/web/lib/events/mail.js
index 62d5478c23..986b81875f 100644
--- a/web/lib/events/mail.js
+++ b/web/lib/events/mail.js
@@ -1,5 +1,3 @@
-load(settings.web_lib + 'forum.js');
-
 var last_run = 0;
 var frequency = 30;
 var last_count = 0;
diff --git a/web/lib/events/sbbsimsg.js b/web/lib/events/sbbsimsg.js
index 34bb30aaea..821dfaf4bf 100644
--- a/web/lib/events/sbbsimsg.js
+++ b/web/lib/events/sbbsimsg.js
@@ -1,5 +1,3 @@
-load("sbbsdefs.js");
-load("nodedefs.js");
 const sbbsimsg = load({}, "sbbsimsg_lib.js");
 
 var last_run = 0;
-- 
GitLab


From e2921b3b772ac9e0ea1b1f1e82d6be8dc178e98e Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:09:47 -0400
Subject: [PATCH 493/752] Some load & require changes.

---
 web/lib/auth.js         | 4 +++-
 web/lib/files.js        | 6 ++++--
 web/lib/forum.js        | 1 +
 web/lib/init.js         | 9 +++++----
 web/lib/locale.js       | 4 ++--
 web/lib/locale/en_ca.js | 2 +-
 6 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 944141a1ab..599120c94b 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -1,4 +1,4 @@
-load('sbbsdefs.js');
+require('sbbsdefs.js', 'SYS_CLOSED');
 
 function randomString(length) {
 	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split("");
@@ -191,4 +191,6 @@ function is_user() {
     }
 })();
 
+const WEBV4_AUTH = true;
+
 this;
diff --git a/web/lib/files.js b/web/lib/files.js
index 34e4c90b7f..859dc1c890 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -1,5 +1,5 @@
-load('filebase.js');
-load('file_size.js');
+require('filebase.js', 'FileBase');
+require('file_size.js', 'file_size_str');
 
 function count_files(dir) {
     var n = 0;
@@ -31,3 +31,5 @@ function listFiles(dir) {
 		return df;
 	});
 }
+
+this;
\ No newline at end of file
diff --git a/web/lib/forum.js b/web/lib/forum.js
index 2fa1a2af2a..c0ccfe33ec 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -1,3 +1,4 @@
+require('sbbsdefs.js', 'MSG_DELETE');
 load(settings.web_lib + 'mime-decode.js');
 
 function listGroups() {
diff --git a/web/lib/init.js b/web/lib/init.js
index 6594453d29..3b8039e9df 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,7 +1,6 @@
-load('sbbsdefs.js');
-load('modopts.js');
+require('sbbsdefs.js', 'SYS_CLOSED');
 
-if (!settings) var settings = get_mod_options('web');
+if (!settings) var settings = load('modopts.js', 'web');
 
 // Paths
 settings.web_directory = fullpath(
@@ -69,7 +68,7 @@ Object.keys(defaults).forEach(function (e) {
 
 defaults = undefined;
 
-load(settings.web_lib + 'locale.js');
+require(settings.web_lib + 'locale.js', 'locale');
 
 if (typeof alert === 'undefined') {
     function alert(msg) {
@@ -95,3 +94,5 @@ const Req = {
     }
   }
 };
+
+const WEBV4_INIT = true;
\ No newline at end of file
diff --git a/web/lib/locale.js b/web/lib/locale.js
index 17ccb6bd60..ff5448479f 100644
--- a/web/lib/locale.js
+++ b/web/lib/locale.js
@@ -1,7 +1,7 @@
 if (!settings.locale) {
-    load(backslash(settings.web_lib + 'locale/') + 'en_us.js');
+    require(backslash(settings.web_lib + 'locale/') + 'en_us.js', 'Locale');
 } else {
-    load(backslash(settings.web_lib + 'locale/') + settings.locale + '.js');
+    require(backslash(settings.web_lib + 'locale/') + settings.locale + '.js', 'Locale');
 }
 
 var locale = new Locale();
diff --git a/web/lib/locale/en_ca.js b/web/lib/locale/en_ca.js
index ef775e2670..705e50728b 100644
--- a/web/lib/locale/en_ca.js
+++ b/web/lib/locale/en_ca.js
@@ -1,4 +1,4 @@
-load(backslash(settings.web_lib + 'locale/') + 'en_us.js');
+require(backslash(settings.web_lib + 'locale/') + 'en_us.js', 'EN_US');
 
 function EN_CA() {
     EN_US.call(this, 'en_ca');
-- 
GitLab


From 418967340f0ac0f9057fa50da9432e06f5997492 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:10:35 -0400
Subject: [PATCH 494/752] Use require(), simplified settings loading. I think
 there's a send_file thing that could be used here now.

---
 web/root/api/files.ssjs | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index d049ddb56d..5821c196ff 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,10 +1,8 @@
-load('modopts.js');
-var settings = get_mod_options('web');
+var settings = load('modopts.js', 'web');
 
-load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
-load(settings.web_lib + 'files.js');
-load('filebase.js');
+require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
+require(settings.web_lib + 'auth.js', 'WEBV4_AUTH');
+require('filebase.js', 'FileBase');
 
 var CHUNK_SIZE = 1024;
 
@@ -22,16 +20,14 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			) {
 				var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
 				var file = null;
-				fileBase.some(
-					function (e) {
-						if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
-							return false;
-						} else if (typeof e.path !== 'undefined') {
-							file = e;
-							return true;
-						}
+				fileBase.some(function (e) {
+					if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
+						return false;
+					} else if (typeof e.path !== 'undefined') {
+						file = e;
+						return true;
 					}
-				);
+				});
 				if (file === null) break;
 				http_reply.header['Content-Type'] = 'application/octet-stream';
 				http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
-- 
GitLab


From b81898bd8d5d6aa1a38aa213b4ef5da6c9170290 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:11:56 -0400
Subject: [PATCH 495/752] Load pages.js and sidebar.js return-style.

---
 web/root/index.xjs | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index bf409412c4..dc1df16f29 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,13 +1,11 @@
 <?xjs
 
-    load('modopts.js');
-    var settings = get_mod_options('web');
+    const settings = load('modopts.js', 'web');
 
-	load('xjs.js');
-	load(settings.web_directory + '/lib/init.js');
+	require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
 	const auth_lib = load({}, settings.web_lib + 'auth.js');
-	load(settings.web_lib + 'pages.js');
-	load(settings.web_lib + 'sidebar.js');
+	const page_lib = load({}, settings.web_lib + 'pages.js');
+	const sidebar_lib = load({}, settings.web_lib + 'sidebar.js');
 
 	var page = typeof http_request.query.page == 'undefined' ? '000-home.xjs' : http_request.query.page[0];
     if (page.search(/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/i) > -1) {
@@ -19,7 +17,7 @@
     if (!file_exists(fullpath(settings.web_pages + page)) || fullpath(settings.web_pages + page).indexOf(fullpath(settings.web_pages)) !== 0) {
         page = '000-home.xjs';
     }
-	var page_ctrl = getCtrlLine(settings.web_pages + page);
+	const page_ctrl = page_lib.getCtrlLine(settings.web_pages + page);
 
 ?>
 
@@ -57,7 +55,7 @@
 		if (settings.layout_sidebar_off || page_ctrl.options.no_sidebar) return;
 ?>
 	<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
-		<?xjs writeSidebarModules(); ?>
+		<?xjs sidebar_lib.writeSidebarModules(); ?>
 	</div>
 <?xjs } ?>
 
@@ -125,7 +123,7 @@
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
 					<ul class="nav navbar-nav">
-						<?xjs _menu(getPageList(settings.web_pages)); ?>
+						<?xjs _menu(page_lib.getPageList(settings.web_pages)); ?>
 					</ul>
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
@@ -191,7 +189,7 @@
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><span class="glyphicon glyphicon-tasks"></span><?xjs write(locale.strings.main.label_sidebar); ?></button>
 					</p>
 					</div>
-					<?xjs writePage(page); ?>
+					<?xjs page_lib.writePage(page); ?>
 				</div>
 				<?xjs if (!settings.layout_sidebar_left || settings.layout_sidebar_right) _sidebar(); ?>
 			</div>
-- 
GitLab


From daa086ea9641defd0eadf7447a0b6fcd7753ab8f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 01:13:25 -0400
Subject: [PATCH 496/752] Changes to use of load() and require() in various
 places.

---
 web/root/api/attachments.ssjs |  9 ++++-----
 web/root/api/auth.ssjs        | 15 +++++++--------
 web/root/api/events.ssjs      |  7 +++----
 3 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index 41d348e5f7..76979a12d4 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -1,10 +1,9 @@
-load('sbbsdefs.js');
+require('sbbsdefs.js', 'SYS_CLOSED');
 
-load('modopts.js');
-var settings = get_mod_options('web');
+var settings = load('modopts.js', 'web');
 
-load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
+require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
+require(settings.web_lib + 'auth.js', 'WEBV4_AUTH');
 load(settings.web_lib + 'mime-decode.js');
 
 function barfOut(err) {
diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index ae124aca58..8d8e6cfa90 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -1,14 +1,13 @@
-load('modopts.js');
-var settings = get_mod_options('web');
+var settings = load('modopts.js', 'web');
 
-load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
+require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
+require(settings.web_lib + 'auth.js', 'WEBV4_AUTH');
 
-var response = JSON.stringify(
-	{ authenticated : (user.alias !== settings.guest) }
-);
+var response = JSON.stringify({
+	authenticated: (user.alias !== settings.guest)
+});
 
 http_reply.header['Content-Type'] = 'application/json';
-//http_reply.header['Content-Length'] = response.length;
+http_reply.header['Content-Length'] = response.length;
 
 write(response);
diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 648b359169..5f8a326246 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,7 +1,6 @@
-load('sbbsdefs.js');
-load('modopts.js');
-const settings = get_mod_options('web');
-load(settings.web_directory + '/lib/init.js');
+require('sbbsdefs.js', 'SYS_CLOSED');
+var settings = load('modopts.js', 'web');
+require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
 const auth_lib = load({}, settings.web_lib + 'auth.js');
 
 js.time_limit = 0;
-- 
GitLab


From efcbdc64f46614d7577b70a49cccdc0c7d714180 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 25 Jul 2019 11:05:10 -0400
Subject: [PATCH 497/752] Always use var for settings.

---
 web/root/index.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index dc1df16f29..fb615b8c25 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,6 +1,6 @@
 <?xjs
 
-    const settings = load('modopts.js', 'web');
+    var settings = load('modopts.js', 'web');
 
 	require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
 	const auth_lib = load({}, settings.web_lib + 'auth.js');
-- 
GitLab


From 2bf987f660344c92f7a37bfbcb9cc5ff36cb42da Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 26 Jul 2019 00:03:24 -0400
Subject: [PATCH 498/752] Uh, everything's broken, so don't nobody update.

---
 web/root/index.xjs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index fb615b8c25..49d63e7de0 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,11 +1,11 @@
 <?xjs
 
-    var settings = load('modopts.js', 'web');
+	var settings = load('modopts.js', 'web');
 
-	require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
-	const auth_lib = load({}, settings.web_lib + 'auth.js');
-	const page_lib = load({}, settings.web_lib + 'pages.js');
-	const sidebar_lib = load({}, settings.web_lib + 'sidebar.js');
+	load(settings.web_directory + '/lib/init.js');
+	var auth_lib = load({}, settings.web_lib + 'auth.js');
+	var page_lib = load({}, settings.web_lib + 'pages.js');
+	var sidebar_lib = load({}, settings.web_lib + 'sidebar.js');
 
 	var page = typeof http_request.query.page == 'undefined' ? '000-home.xjs' : http_request.query.page[0];
     if (page.search(/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/i) > -1) {
@@ -17,7 +17,7 @@
     if (!file_exists(fullpath(settings.web_pages + page)) || fullpath(settings.web_pages + page).indexOf(fullpath(settings.web_pages)) !== 0) {
         page = '000-home.xjs';
     }
-	const page_ctrl = page_lib.getCtrlLine(settings.web_pages + page);
+	var page_ctrl = page_lib.getCtrlLine(settings.web_pages + page);
 
 ?>
 
-- 
GitLab


From 7787dde205aad87ef4ffca4931a2a155c7ef06dc Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 29 Jul 2019 00:37:53 -0400
Subject: [PATCH 499/752] Moved Req from lib/init.js and renamed to Request.

---
 web/lib/request.js | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 web/lib/request.js

diff --git a/web/lib/request.js b/web/lib/request.js
new file mode 100644
index 0000000000..945fae6a54
--- /dev/null
+++ b/web/lib/request.js
@@ -0,0 +1,18 @@
+// Helpers for http_request
+const Request = {
+    // Query parameter p exists and first instance is of optional type t
+    has_param: function (p) {
+      return (Array.isArray(http_request.query[p]) && http_request.query[p].length);
+    },
+    // First instance of query parameter p, or undefined
+    get_param: function (p) {
+      if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
+          return http_request.query[p][0];
+      }
+    },
+    write_param: function (p) {
+      if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
+          write(http_request.query[p][0]);
+      }
+    }
+};
-- 
GitLab


From e1c36416a20448b56bac9c59c57614436cacfdc4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 29 Jul 2019 00:43:13 -0400
Subject: [PATCH 500/752] Fix much of what was broken a few days ago. Execute
 most any top-level SSJS in an IIFE to work around whatever context reuse
 thing is happening. I'm aboot done with this shit for now I tell you what.

---
 web/lib/auth.js                   |   4 -
 web/lib/events/forum.js           |  16 +-
 web/lib/events/nodelist.js        |   2 +-
 web/lib/files.js                  |   2 -
 web/lib/forum.js                  |  12 +-
 web/lib/ftelnet.js                |   2 -
 web/lib/init.js                   |  29 --
 web/lib/pages.js                  |   8 +-
 web/lib/sidebar.js                |   6 +-
 web/pages/.examples/000-home.xjs  |  12 +-
 web/pages/.examples/001-forum.xjs | 150 +++++-----
 web/pages/.examples/002-files.xjs |  33 +-
 web/pages/.examples/003-games.xjs |   4 +-
 web/root/api/attachments.ssjs     |   6 +-
 web/root/api/auth.ssjs            |  20 +-
 web/root/api/events.ssjs          |  97 +++---
 web/root/api/files.ssjs           | 108 +++----
 web/root/api/forum.ssjs           | 480 +++++++++++++++---------------
 web/root/api/github.ssjs          | 144 ++++-----
 web/root/api/register.ssjs        | 434 +++++++++++++--------------
 web/root/api/sbbsimsg.ssjs        |  51 ++--
 web/root/api/system.ssjs          | 167 +++++------
 web/root/index.xjs                |  24 +-
 23 files changed, 897 insertions(+), 914 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 599120c94b..ac80ef7952 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -190,7 +190,3 @@ function is_user() {
     	}
     }
 })();
-
-const WEBV4_AUTH = true;
-
-this;
diff --git a/web/lib/events/forum.js b/web/lib/events/forum.js
index f474bbba2c..96bf879c08 100644
--- a/web/lib/events/forum.js
+++ b/web/lib/events/forum.js
@@ -1,4 +1,4 @@
-const forum_lib = load({}, settings.web_lib + 'forum.js');
+load(settings.web_lib + 'forum.js');
 
 var last_subs;
 var last_groups;
@@ -24,7 +24,7 @@ function forum_emit(evt, data) {
 }
 
 function scan_groups() {
-    const scan = forum_lib.getGroupUnreadCounts();
+    const scan = getGroupUnreadCounts();
     if (!last_groups) {
         forum_emit('groups_unread', scan);
     } else {
@@ -35,7 +35,7 @@ function scan_groups() {
 }
 
 function scan_subs(group) {
-    const scan = forum_lib.getSubUnreadCounts(group);
+    const scan = getSubUnreadCounts(group);
     if (!last_subs) {
         forum_emit('subs_unread', scan);
     } else {
@@ -46,7 +46,7 @@ function scan_subs(group) {
 }
 
 function scan_threads(sub, offset, page_size) {
-    const scan = forum_lib.getThreadStats(sub, offset, page_size);
+    const scan = getThreadStats(sub, offset, page_size);
     if (!last_threads) {
         forum_emit('threads', scan);
     } else {
@@ -66,12 +66,12 @@ function scan_threads(sub, offset, page_size) {
 }
 
 function cycle() {
-    if (!auth_lib.is_user()) return;
+    if (!is_user()) return;
     if (time() - last_run <= frequency) return;
     last_run = time();
-    if (Req.has_param('groups_unread')) scan_groups();
-    if (Req.has_param('subs_unread')) scan_subs(Req.get_param('subs_unread'));
-    if (Req.has_param('threads')) scan_threads(Req.get_param('threads'), Req.get_param('offset'), Req.get_param('page_size'));
+    if (Request.has_param('groups_unread')) scan_groups();
+    if (Request.has_param('subs_unread')) scan_subs(Request.get_param('subs_unread'));
+    if (Request.has_param('threads')) scan_threads(Request.get_param('threads'), Request.get_param('offset'), Request.get_param('page_size'));
 }
 
 this;
diff --git a/web/lib/events/nodelist.js b/web/lib/events/nodelist.js
index 2efc678edd..a3f931f61e 100644
--- a/web/lib/events/nodelist.js
+++ b/web/lib/events/nodelist.js
@@ -50,7 +50,7 @@ function scan_web() {
                 change = true;
             }
         } else {
-            const action = auth_lib.getSessionValue(un, 'action');
+            const action = getSessionValue(un, 'action');
             if (web_state[base] != action) {
                 web_state[base] = action;
                 change = true;
diff --git a/web/lib/files.js b/web/lib/files.js
index 859dc1c890..a4fc22ba81 100644
--- a/web/lib/files.js
+++ b/web/lib/files.js
@@ -31,5 +31,3 @@ function listFiles(dir) {
 		return df;
 	});
 }
-
-this;
\ No newline at end of file
diff --git a/web/lib/forum.js b/web/lib/forum.js
index c0ccfe33ec..b23f8f21f5 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -930,7 +930,7 @@ function getMessageThreads(sub, max) {
         if (header.when_written_time > threads.thread[thread_id].newest) {
             threads.thread[thread_id].newest = header.when_written_time;
         }
-        if (auth_lib.is_user() && header.number > msg_area.sub[sub].scan_ptr) {
+        if (is_user() && header.number > msg_area.sub[sub].scan_ptr) {
             threads.thread[thread_id].unread++;
         }
         threads.thread[thread_id].messages[header.number] = {
@@ -1122,16 +1122,14 @@ function getMessageThreads(sub, max) {
 }
 
 function isValidRequest() {
-    if (Req.has_param('group')) {
-        const grp = Req.get_param('group');
+    if (Request.has_param('group')) {
+        const grp = Request.get_param('group');
         if (typeof msg_area.grp_list[grp] == 'undefined') return false;
         if (!user.compare_ars(msg_area.grp_list[grp].ars)) return false;
     }
-    if (Req.has_param('sub')) {
-        const sub = Req.get_param('sub');
+    if (Request.has_param('sub')) {
+        const sub = Request.get_param('sub');
         if (typeof msg_area.sub[sub] == 'undefined') return false;
     }
     return true;
 }
-
-this;
diff --git a/web/lib/ftelnet.js b/web/lib/ftelnet.js
index 663ebf7937..edcb2318ad 100644
--- a/web/lib/ftelnet.js
+++ b/web/lib/ftelnet.js
@@ -48,5 +48,3 @@ function get_url() {
     }
     return get_cached_url(f);
 }
-
-this;
diff --git a/web/lib/init.js b/web/lib/init.js
index 3b8039e9df..9190c933cc 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -1,7 +1,5 @@
 require('sbbsdefs.js', 'SYS_CLOSED');
 
-if (!settings) var settings = load('modopts.js', 'web');
-
 // Paths
 settings.web_directory = fullpath(
 	backslash(
@@ -69,30 +67,3 @@ Object.keys(defaults).forEach(function (e) {
 defaults = undefined;
 
 require(settings.web_lib + 'locale.js', 'locale');
-
-if (typeof alert === 'undefined') {
-    function alert(msg) {
-        log(LOG_ERR, msg);
-    }
-}
-
-// Helpers for http_request
-const Req = {
-  // Query parameter p exists and first instance is of optional type t
-  has_param: function (p) {
-    return (Array.isArray(http_request.query[p]) && http_request.query[p].length);
-  },
-  // First instance of query parameter p, or undefined
-  get_param: function (p) {
-    if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
-        return http_request.query[p][0];
-    }
-  },
-  write_param: function (p) {
-    if (Array.isArray(http_request.query[p]) && http_request.query[p].length) {
-        write(http_request.query[p][0]);
-    }
-  }
-};
-
-const WEBV4_INIT = true;
\ No newline at end of file
diff --git a/web/lib/pages.js b/web/lib/pages.js
index bca8086c88..e26519f7e0 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -145,7 +145,7 @@ function getPage(page) {
 	if (user.alias != settings.guest) {
 		var ctrl = getCtrlLine(page);
 		if (typeof ctrl !== 'undefined' && !ctrl.options.hidden) {
-			auth_lib.setSessionValue(user.number, 'action', ctrl.title);
+			setSessionValue(user.number, 'action', ctrl.title);
 		}
 	}
 
@@ -153,12 +153,12 @@ function getPage(page) {
 		case '.SSJS':
 			if (ext === '.SSJS' && page.search(/\.xjs\.ssjs$/i) >= 0) break;
 			(function () {
-				load({}, page, true);
+				load(page, true);
 			})();
 			break;
 		case '.XJS':
 			(function () {
-				load({}, xjs_compile(page), true);
+				load(xjs_compile(page), true);
 			})();
 			break;
 		case '.HTML':
@@ -191,5 +191,3 @@ function writePage(page) {
 		write(getPage(page));
 	}
 }
-
-this;
\ No newline at end of file
diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index 3de128415e..5550bc28d9 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -24,12 +24,12 @@ function getSidebarModule(module) {
 		case '.SSJS':
 			if (ext === '.SSJS' && module.search(/\.xjs\.ssjs$/i) >= 0) break;
 			(function () {
-				load({}, module, true);
+				load(module, true);
 			})();
 			break;
 		case '.XJS':
 			(function () {
-				load({}, xjs_compile(module), true);
+				load(xjs_compile(module), true);
 			})();
 			break;
 		case '.HTML':
@@ -60,5 +60,3 @@ function writeSidebarModules() {
 	);
 	write('</ul>');
 }
-
-this;
\ No newline at end of file
diff --git a/web/pages/.examples/000-home.xjs b/web/pages/.examples/000-home.xjs
index 4dfe6ae674..32b5353df8 100644
--- a/web/pages/.examples/000-home.xjs
+++ b/web/pages/.examples/000-home.xjs
@@ -3,7 +3,7 @@
 <!-- fTelnet -->
 <?xjs
     if (settings.ftelnet) {
-        const ftelnet_lib = load({}, settings.web_lib + 'ftelnet.js');
+        load(settings.web_lib + 'ftelnet.js');
         load('ftelnethelper.js');
 ?>
     	<div id="fTelnetContainer" class="fTelnetContainer"></div>
@@ -18,13 +18,14 @@
             </div>
         <?xjs } ?>
 
-    	<script id="fTelnetScript" src="<?xjs write(ftelnet_lib.get_url()); ?>"></script>
+    	<script id="fTelnetScript" src="<?xjs write(get_url()); ?>"></script>
     	<script>
             var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
             var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;
     		var Options = new fTelnetOptions();
     		Options.BareLFtoCRLF = false;
-    		Options.BitsPerSecond = 57600;
+            Options.BitsPerSecond = 57600;
+            Options.ButtonBarVisible = true;
     		Options.ConnectionType = 'telnet';
     		Options.Emulation = 'ansi-bbs';
     		Options.Enter = '\r';
@@ -35,8 +36,9 @@
     		Options.Port = location.protocol == 'https:' ? wssp : wsp;
     		Options.ScreenColumns = 80;
     		Options.ScreenRows = 25;
-            Options.SplashScreen = '<?xjs write(ftelnet_lib.get_splash()); ?>';
-    		var fTelnet = new fTelnetClient('fTelnetContainer', Options);
+            Options.SplashScreen = '<?xjs write(get_splash()); ?>';
+            var fTelnet = new fTelnetClient('fTelnetContainer', Options);
+            fTelnet.ButtonBarVisible = true;
             if ($('#ftelnet-connect').length) {
                 $('#ftelnet-connect').click(function() {
                     fTelnet.Connect();
diff --git a/web/pages/.examples/001-forum.xjs b/web/pages/.examples/001-forum.xjs
index d9160cab73..2f40948612 100644
--- a/web/pages/.examples/001-forum.xjs
+++ b/web/pages/.examples/001-forum.xjs
@@ -3,42 +3,43 @@
 <script type="text/javascript" src="./js/forum.js"></script>
 
 <?xjs
-    const forum_lib = load({}, settings.web_lib + 'forum.js');
+    load(settings.web_lib + 'request.js');
+    load(settings.web_lib + 'forum.js');
     locale.section = 'page_forum';
 ?>
 
-<!-- To do: Search (forum, group, sub, or thread) -->
+<!-- To do: Search (forum, group, sub, or thread), Sortation and data-attributes -->
 
-<?xjs if (Req.has_param('sub') && Req.has_param('thread')) { ?>
+<?xjs if (Request.has_param('sub') && Request.has_param('thread')) { ?>
+
+    <?xjs
+        var offset = Request.has_param('offset') ? parseInt(Request.get_param('offset')) : 0;
+        const thread = getMessageThreads(Request.get_param('sub'), settings.max_messages).thread[Request.get_param('thread')];
+    ?>
 
     <ol class="breadcrumb">
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>">
+            <a href="./?page=<? Request.write_param('page'); ?>">
                 <? locale.write('title'); ?>
             </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>">
-                <? write(msg_area.grp_list[msg_area.sub[Req.get_param('sub')].grp_index].name); ?>
+            <a href="./?page=<? Request.write_param('page'); ?>&group=<? Request.write_param('group'); ?>">
+                <? write(msg_area.grp_list[msg_area.sub[Request.get_param('sub')].grp_index].name); ?>
             </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>">
-                <? write(msg_area.sub[Req.get_param('sub')].name); ?>
+            <a href="./?page=<? Request.write_param('page'); ?>&sub=<? Request.write_param('sub'); ?>">
+                <? write(msg_area.sub[Request.get_param('sub')].name); ?>
             </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>&thread=<? Req.write_param('thread'); ?>">
-                Thread <? Req.write_param('thread'); ?>
+            <a href="./?page=<? Request.write_param('page'); ?>&sub=<? Request.write_param('sub'); ?>&thread=<? Request.write_param('thread'); ?>">
+                <? write(thread.subject); ?>
             </a>
         </li>
     </ol>
 
-    <?xjs
-        var offset = Req.has_param('offset') ? parseInt(Req.get_param('offset')) : 0;
-        const thread = forum_lib.getMessageThreads(Req.get_param('sub'), settings.max_messages);
-    ?>
-
     <div id="jump-unread-container" style="margin-bottom:1em;">
         <a class="btn btn-default" id="jump-unread" title="Jump to first unread message" href="#">
             <span class="glyphicon glyphicon-star"></span>
@@ -49,30 +50,30 @@
 
     </div>
 
-<?xjs } else if (Req.has_param('sub') && typeof msg_area.sub[Req.get_param('sub')] != 'undefined') { ?>
+<?xjs } else if (Request.has_param('sub') && typeof msg_area.sub[Request.get_param('sub')] != 'undefined') { ?>
 
     <ol class="breadcrumb">
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>">
+            <a href="./?page=<? Request.write_param('page'); ?>">
                 <? locale.write('title'); ?>
             </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(msg_area.grp_list[msg_area.sub[Req.get_param('sub')].grp_index].number); ?>">
-                <? write(msg_area.grp_list[msg_area.sub[Req.get_param('sub')].grp_index].name); ?>
+            <a href="./?page=<? Request.write_param('page'); ?>&group=<? write(msg_area.grp_list[msg_area.sub[Request.get_param('sub')].grp_index].number); ?>">
+                <? write(msg_area.grp_list[msg_area.sub[Request.get_param('sub')].grp_index].name); ?>
             </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>">
-                <? write(msg_area.sub[Req.get_param('sub')].name); ?>
+            <a href="./?page=<? Request.write_param('page'); ?>&sub=<? Request.write_param('sub'); ?>">
+                <? write(msg_area.sub[Request.get_param('sub')].name); ?>
             </a>
         </li>
     </ol>
 
     <?xjs
-        var offset = Req.has_param('offset') ? parseInt(Req.get_param('offset')) : 0;
-        const threads = forum_lib.listThreads(Req.get_param('sub'), offset, settings.page_size);
-        const sub = msg_area.sub[Req.get_param('sub')];
+        var offset = Request.has_param('offset') ? parseInt(Request.get_param('offset')) : 0;
+        const threads = listThreads(Request.get_param('sub'), offset, settings.page_size, true) || { total: 0, threads: [] };
+        const sub = msg_area.sub[Request.get_param('sub')];
     ?>
 
     <button class="btn btn-default icon" aria-label="<? locale.write('button_post_message'); ?>" title="<? locale.write('button_post_message'); ?>" onclick="addNew('<? write(sub.code); ?>')">
@@ -94,48 +95,41 @@
         </button>
     </div>
 
+    <div id="forum-thread-replies-template" hidden>
+        <p>
+            <strong data-message-count></strong>
+            <span data-suffix-reply hidden><? locale.write('suffix_reply_count'); ?>.</span>
+            <span data-suffix-replies hidden><? locale.write('suffix_replies_count'); ?>.</span>
+            <? locale.write('label_thread_latest_reply'); ?>
+            <strong data-last-from></strong>
+            <span data-last-time></span>
+        </p>
+    </div>
+
+    <div id="forum-thread-stats-template" hidden>
+        <?xjs if (is_user()) { ?>
+            <span data-unread-messages title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : ''); ?>" hidden></span>
+        <?xjs } ?>
+        <span data-upvotes-badge title="<? locale.write('badge_upvotes'); ?>" class="badge upvote-bg" style="display:none;">
+            <span class="glyphicon glyphicon-arrow-up"></span>
+            <span data-upvotes></span>
+        </span>
+        <span data-downvotes-badge title="<? locale.write('badge_downvotes'); ?>" class="badge downvote-bg" style="display:none;">
+            <span class="glyphicon glyphicon-arrow-down"></span>
+            <span data-downvotes></span>
+        </span>
+    </div>
+
     <div id="forum-list-container" class="list-group">
         <?xjs threads.threads.forEach(function (e) { ?>
-            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? Req.write_param('sub'); ?>&thread=<? write(e.id); ?>" class="list-group-item striped">
+            <a href="./?page=<? Request.write_param('page'); ?>&sub=<? Request.write_param('sub'); ?>&thread=<? write(e.id); ?>" class="list-group-item striped">
                 <div class="row">
-                    <div class="col-sm-9">
+                    <div id="left-<? write(e.id); ?>" class="col-sm-9">
                         <strong><? write(e.subject); ?></strong>
                         <p><strong><? write(e.first.from); ?></strong>, <? write(system.timestr(e.first.when_written_time)); ?></p>
-                        <?xjs if (e.messages > 1) { ?>
-                            <p>
-                                <strong><? write(e.messages - 1); ?></strong>
-                                <?xjs
-                                    if (e.messages == 2) {
-                                        locale.write('suffix_reply_count');
-                                    } else {
-                                        locale.write('suffix_replies_count');
-                                    }
-                                ?>,
-                                <? locale.write('label_thread_latest_reply'); ?>
-                                <strong><? write(e.last.from); ?></strong>,
-                                <? write(system.timestr(e.last.when_written_time)); ?>
-                            </p>
-                        <?xjs } ?>
                     </div>
                     <div class="col-sm-3">
-                        <div class="pull-right">
-                            <?xjs if (auth_lib.is_user() && e.unread) { ?>
-                                <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(sub.scan_cfg&SCAN_CFG_NEW || sub.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : ''); ?>" id="badge-<? write(e.id); ?>">
-                                    <? write(e.unread); ?>
-                                </span>
-                            <?xjs } ?>
-                            <?xjs if (e.votes.up || e.votes.down) { ?>
-                                <span id="uv-<? write(e.id); ?>" title="<? locale.write('badge_upvotes'); ?>" class="badge upvote-bg">
-                                    <span class="glyphicon glyphicon-arrow-up"></span>
-                                    <span id="uv-count-<? write(e.id); ?>"><? write(e.first.upvotes + ' / ' + e.votes.up); ?></span>
-                                </span>
-                                &nbsp;
-                                <span id="dv-<? write(e.id); ?>" title="<? locale.write('badge_downvotes'); ?>" class="badge downvote-bg">
-                                    <span class="glyphicon glyphicon-arrow-down"></span>
-                                    <span id="dv-count-<? write(e.id); ?>"><? write(e.first.downvotes + ' / ' + e.votes.down); ?></span>
-                                </span>
-                            <?xjs } ?>
-                        </div>
+                        <div id="right-<? write(e.id); ?>" class="pull-right"></div>
                     </div>
                 </div>
             </a>
@@ -176,29 +170,37 @@
         </div class="btn-group">
     <?xjs } ?>
 
-    <?xjs if (auth_lib.is_user()) { ?>
-        <!-- To do: Subscribe to vote, unread events -->
-    <?xjs } ?>
+    <script type="text/javascript">
+        registerEventListener('forum', function (e) {
+            const data = JSON.parse(e.data);
+            if (data.type != 'threads') return;
+            onThreadStats(data.data);
+        }, {
+            threads: '<? Request.write_param("sub"); ?>',
+            offset: '<? write(offset); ?>',
+            page_size: '<? write(settings.page_size); ?>'
+        });
+    </script>
 
-<?xjs } else if (Req.has_param('group') && typeof msg_area.grp_list[Req.get_param('group')] != 'undefined') { ?>
+<?xjs } else if (Request.has_param('group') && typeof msg_area.grp_list[Request.get_param('group')] != 'undefined') { ?>
 
     <ol class="breadcrumb">
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>">
+            <a href="./?page=<? Request.write_param('page'); ?>">
                 <? locale.write('title'); ?>
             </a>
         </li>
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? Req.write_param('group'); ?>">
-                <? write(msg_area.grp_list[Req.get_param('group')].name); ?>
+            <a href="./?page=<? Request.write_param('page'); ?>&group=<? Request.write_param('group'); ?>">
+                <? write(msg_area.grp_list[Request.get_param('group')].name); ?>
             </a>
         </li>
     </ol>
 
     <div id="forum-list-container" class="list-group">
-        <?xjs const subs = forum_lib.listSubs(Req.get_param('group')); ?>
+        <?xjs const subs = listSubs(Request.get_param('group')); ?>
         <?xjs subs.forEach(function (e) { ?>
-            <a href="./?page=<? Req.write_param('page'); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
+            <a href="./?page=<? Request.write_param('page'); ?>&sub=<? write(e.code); ?>" class="list-group-item striped">
                 <h4><strong><? write(e.name); ?></strong></h4>
                 <span title="<? locale.write('badge_unread_messages'); ?>" class="badge <? write(e.scan_cfg&SCAN_CFG_NEW || e.scan_cfg&SCAN_CFG_YONLY ? 'scanned' : 'total'); ?>" id="badge-<? write(e.code); ?>"></span>
                 <p><? write(e.description); ?></p>
@@ -206,13 +208,13 @@
         <?xjs }); ?>
     </div>
 
-    <?xjs if (auth_lib.is_user()) { ?>
+    <?xjs if (is_user()) { ?>
         <script type="text/javascript">
             registerEventListener('forum', function (e) {
                 const data = JSON.parse(e.data);
                 if (data.type != 'subs_unread') return;
                 onSubUnreadCount(data.data);
-             }, { subs_unread: '<? Req.write_param('group'); ?>' });
+             }, { subs_unread: '<? Request.write_param("group"); ?>' });
         </script>
     <?xjs } ?>
 
@@ -220,14 +222,14 @@
 
     <ol class="breadcrumb">
         <li>
-            <a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+            <a href="./?page=<? Request.write_param('page'); ?>"><? locale.write('title'); ?></a>
         </li>
     </ol>
 
     <div id="forum-list-container" class="list-group">
-        <?xjs const groups = forum_lib.listGroups(); ?>
+        <?xjs const groups = listGroups(); ?>
         <?xjs groups.forEach(function (e) { ?>
-            <a href="./?page=<? Req.write_param('page'); ?>&group=<? write(e.index); ?>" class="list-group-item striped">
+            <a href="./?page=<? Request.write_param('page'); ?>&group=<? write(e.index); ?>" class="list-group-item striped">
                 <h3><strong><? write(e.name); ?></strong></h3>
                 <span title="<? locale.write('badge_unread_messages'); ?>" class="badge ignored" id="badge-ignored-<? write(e.index); ?>"></span>
                 <span title="<? locale.write('badge_unread_messages'); ?>" class="badge scanned" id="badge-scanned-<? write(e.index); ?>"></span>
@@ -236,7 +238,7 @@
         <?xjs }); ?>
     </div>
 
-    <?xjs if (auth_lib.is_user()) { ?>
+    <?xjs if (is_user()) { ?>
         <script type="text/javascript">
             registerEventListener('forum', function (e) {
                 const data = JSON.parse(e.data);
diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index 90dff69e2d..2175252c76 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -1,30 +1,31 @@
 <!--Files-->
 
 <?xjs
+	load(settings.web_lib + 'request.js');
 	load(settings.web_lib + 'files.js');
     locale.section = 'page_files';
-	if (Req.has_param('dir')) {
+	if (Request.has_param('dir')) {
 ?>
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? Req.write_param('page'); ?>">
+			<a href="./?page=<? Request.write_param('page'); ?>">
 				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? Req.write_param('page'); ?>&amp;library=<? write(file_area.dir[Req.get_param('dir')].lib_index); ?>">
-				<? write(file_area.dir[Req.get_param('dir')].lib_name); ?>
+			<a href="./?page=<? Request.write_param('page'); ?>&amp;library=<? write(file_area.dir[Request.get_param('dir')].lib_index); ?>">
+				<? write(file_area.dir[Request.get_param('dir')].lib_name); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? Req.write_param('page'); ?>&amp;dir=<? Req.write_param('dir'); ?>">
-				<? Req.write_param('dir'); ?>
+			<a href="./?page=<? Request.write_param('page'); ?>&amp;dir=<? Request.write_param('dir'); ?>">
+				<? Request.write_param('dir'); ?>
 			</a>
 		</li>
 	</ol>
 
 	<?xjs function writeFileDetails(file) { ?>
-		<a href="./api/files.ssjs?call=download-file&amp;dir=<? Req.write_param('dir'); ?>&amp;file=<? write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<? write(file._size); ?>" data-uploaded="<? write(file.uldate); ?>" data-name="<? write(file.name); ?>">
+		<a href="./api/files.ssjs?call=download-file&amp;dir=<? Request.write_param('dir'); ?>&amp;file=<? write(file.name); ?>" target="_blank" class="list-group-item striped" data-file-list-element data-size="<? write(file._size); ?>" data-uploaded="<? write(file.uldate); ?>" data-name="<? write(file.name); ?>">
 			<strong><? write(file.name); ?></strong> (<? write(file.size); ?>)
 			<p><em>Uploaded <? write(system.timestr(file.uldate)); ?></em></p>
 			<?xjs if (typeof file.extdesc === 'undefined') { ?>
@@ -53,27 +54,27 @@
 		</div>
 	</div>
 	<div id="file-list-container" class="list-group">
-	<?xjs listFiles(Req.get_param('dir')).forEach(writeFileDetails); ?>
+	<?xjs listFiles(Request.get_param('dir')).forEach(writeFileDetails); ?>
 	</div>
 
-<?xjs } else if (Req.has_param('library')) { ?>
+<?xjs } else if (Request.has_param('library')) { ?>
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? Req.write_param('page'); ?>">
+			<a href="./?page=<? Request.write_param('page'); ?>">
 				<? locale.write('title'); ?>
 			</a>
 		</li>
 		<li>
-			<a href="./?page=<? Req.write_param('page'); ?>&amp;library=<? Req.write_param('library'); ?>">
-				<? write(file_area.lib_list[Req.get_param('library')].name); ?>
+			<a href="./?page=<? Request.write_param('page'); ?>&amp;library=<? Request.write_param('library'); ?>">
+				<? write(file_area.lib_list[Request.get_param('library')].name); ?>
 			</a>
 		</li>
 	</ol>
 
 
 	<?xjs function writeDirectory(dir) { ?>
-		<a href="./?page=<? Req.write_param('page'); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
+		<a href="./?page=<? Request.write_param('page'); ?>&amp;dir=<? write(dir.dir.code); ?>" class="list-group-item striped">
 			<h4><strong><? write(dir.dir.name); ?></strong></h4>
 			<p>
 				<? write(dir.dir.description); ?>:
@@ -84,19 +85,19 @@
 	<?xjs } ?>
 
 	<div id="file-list-container" class="list-group">
-		<?xjs listDirectories(Req.get_param('library')).forEach(writeDirectory); ?>
+		<?xjs listDirectories(Request.get_param('library')).forEach(writeDirectory); ?>
 	</div>
 
 <?xjs } else { ?>
 
 	<ol class="breadcrumb">
 		<li>
-			<a href="./?page=<? Req.write_param('page'); ?>"><? locale.write('title'); ?></a>
+			<a href="./?page=<? Request.write_param('page'); ?>"><? locale.write('title'); ?></a>
 		</li>
 	</ol>
 
 	<?xjs function writeLibrary(library) { ?>
-		<a href="./?page=<? Req.write_param('page'); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
+		<a href="./?page=<? Request.write_param('page'); ?>&amp;library=<? write(library.index); ?>" class="list-group-item striped">
 			<h3><strong><? write(library.name); ?></strong></h3>
 			<p>
 				<? write(library.description); ?>:
diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index 2fa624aea8..e92ed5076f 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -2,7 +2,7 @@
 <?xjs
 
     load("ftelnethelper.js");
-    const ftelnet_lib = load({}, settings.web_lib + 'ftelnet.js');
+    load(settings.web_lib + 'ftelnet.js');
 
     if (typeof settings.xtrn_blacklist === 'string') {
         settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(',');
@@ -60,7 +60,7 @@
     Options.RLoginServerUsername = '<?xjs write(user.alias); ?>';
     Options.ScreenColumns = 80;
     Options.ScreenRows = 25;
-    Options.SplashScreen = Options.SplashScreen = '<?xjs write(ftelnet_lib.get_splash()); ?>';
+    Options.SplashScreen = Options.SplashScreen = '<?xjs write(get_splash()); ?>';
     Options.WebSocketUrlPath = '?Port=<?xjs write(GetRLoginPort()); ?>';
     var fTelnet = new fTelnetClient('fTelnetContainer', Options);
     fTelnet.OnConnectionClose = function () {
diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index 76979a12d4..77b4b6d523 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -1,9 +1,9 @@
 require('sbbsdefs.js', 'SYS_CLOSED');
 
-var settings = load('modopts.js', 'web');
+const settings = load('modopts.js', 'web');
 
-require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
-require(settings.web_lib + 'auth.js', 'WEBV4_AUTH');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'mime-decode.js');
 
 function barfOut(err) {
diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 8d8e6cfa90..1e5dac3b90 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -1,13 +1,15 @@
-var settings = load('modopts.js', 'web');
+(function () {
+	const settings = load('modopts.js', 'web');
 
-require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
-require(settings.web_lib + 'auth.js', 'WEBV4_AUTH');
+	load(settings.web_directory + '/lib/init.js');
+	load(settings.web_lib + 'auth.js');
 
-var response = JSON.stringify({
-	authenticated: (user.alias !== settings.guest)
-});
+	var response = JSON.stringify({
+		authenticated: (user.alias !== settings.guest)
+	});
 
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = response.length;
+	http_reply.header['Content-Type'] = 'application/json';
+	http_reply.header['Content-Length'] = response.length;
 
-write(response);
+	write(response);
+})();
\ No newline at end of file
diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 5f8a326246..07f4d78e76 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,57 +1,62 @@
-require('sbbsdefs.js', 'SYS_CLOSED');
-var settings = load('modopts.js', 'web');
-require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
-const auth_lib = load({}, settings.web_lib + 'auth.js');
+//(function () {
+    
+    load('sbbsdefs.js');
+    const settings = load('modopts.js', 'web');
+    load(settings.web_directory + '/lib/init.js');
+    load(settings.web_lib + 'auth.js');
+    load(settings.web_lib + 'request.js');
 
-js.time_limit = 0;
+    js.time_limit = 0;
 
-http_reply.header['Cache-Control'] = 'no-cache';
-http_reply.header['Content-type'] = 'text/event-stream';
-http_reply.header['X-Accel-Buffering'] = 'no'; // probably not needed by everyone (nginx)
+    http_reply.header['Cache-Control'] = 'no-cache';
+    http_reply.header['Content-type'] = 'text/event-stream';
+    http_reply.header['X-Accel-Buffering'] = 'no'; // probably not needed by everyone (nginx)
 
-const keepalive = 15;
-var last_send = 0;
+    const keepalive = 15;
+    var last_send = 0;
 
-function ping() {
-    if (time() - last_send > keepalive) {
-        write(': ping\n\n');
+    function ping() {
+        if (time() - last_send > keepalive) {
+            write(': ping\n\n');
+            last_send = time();
+        }
+    }
+
+    function emit(obj) {
+        Object.keys(obj).forEach(function (e) {
+            write(e + ': ' + (typeof obj[e] == 'object' ? JSON.stringify(obj[e]) : obj[e]) + '\n');
+        });
+        write('\n');
         last_send = time();
     }
-}
-
-function emit(obj) {
-    Object.keys(obj).forEach(function (e) {
-        write(e + ': ' + (typeof obj[e] == 'object' ? JSON.stringify(obj[e]) : obj[e]) + '\n');
-    });
-    write('\n');
-    last_send = time();
-}
-
-const callbacks = {};
-if (file_isdir(settings.web_lib + 'events')) {
-    if (Array.isArray(http_request.query.subscribe)) {
-        http_request.query.subscribe.forEach(function (e) {
-            const base = file_getname(e).replace(file_getext(e), '');
-            const script = settings.web_lib + 'events/' + base + '.js';
+
+    const callbacks = {};
+    if (file_isdir(settings.web_lib + 'events')) {
+        if (Array.isArray(http_request.query.subscribe)) {
+            http_request.query.subscribe.forEach(function (e) {
+                const base = file_getname(e).replace(file_getext(e), '');
+                const script = settings.web_lib + 'events/' + base + '.js';
+                try {
+                    if (file_exists(script)) callbacks[e] = load({}, script);
+                } catch (err) {
+                   log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
+                }
+            });
+        }
+    }
+
+    ping();
+    while (client.socket.is_connected) {
+        Object.keys(callbacks).forEach(function (e) {
             try {
-                if (file_exists(script)) callbacks[e] = load({}, script);
+                callbacks[e].cycle();
             } catch (err) {
-               log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
+               log(LOG_ERR, 'Callback ' + e + ' failed: ' + err);
+               delete callbacks[e];
             }
         });
+        yield();
+        ping();
     }
-}
-
-ping();
-while (client.socket.is_connected) {
-    Object.keys(callbacks).forEach(function (e) {
-        try {
-            callbacks[e].cycle();
-        } catch (err) {
-            log(LOG_ERR, 'Callback ' + e + ' failed: ' + err);
-            delete callbacks[e];
-        }
-    });
-    yield();
-    ping();
-}
+
+//})();
\ No newline at end of file
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index 5821c196ff..c54fc512e4 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,58 +1,62 @@
-var settings = load('modopts.js', 'web');
-
-require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
-require(settings.web_lib + 'auth.js', 'WEBV4_AUTH');
-require('filebase.js', 'FileBase');
-
-var CHUNK_SIZE = 1024;
-
-var reply = {};
-if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-	typeof http_request.query.call !== 'undefined' && user.number > 0
-) {
-
-	switch (http_request.query.call[0].toLowerCase()) {
-		case 'download-file':
-			if (typeof http_request.query.dir !== 'undefined' &&
-				typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
-				file_area.dir[http_request.query.dir[0]].can_download &&
-				typeof http_request.query.file !== 'undefined'
-			) {
-				var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
-				var file = null;
-				fileBase.some(function (e) {
-					if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
-						return false;
-					} else if (typeof e.path !== 'undefined') {
-						file = e;
-						return true;
+(function () {
+
+	const settings = load('modopts.js', 'web');
+
+	load(settings.web_directory + '/lib/init.js');
+	load(settings.web_lib + 'auth.js');
+	require('filebase.js', 'FileBase');
+
+	var CHUNK_SIZE = 1024;
+
+	var reply = {};
+	if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+		typeof http_request.query.call !== 'undefined' && user.number > 0
+	) {
+
+		switch (http_request.query.call[0].toLowerCase()) {
+			case 'download-file':
+				if (typeof http_request.query.dir !== 'undefined' &&
+					typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
+					file_area.dir[http_request.query.dir[0]].can_download &&
+					typeof http_request.query.file !== 'undefined'
+				) {
+					var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
+					var file = null;
+					fileBase.some(function (e) {
+						if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
+							return false;
+						} else if (typeof e.path !== 'undefined') {
+							file = e;
+							return true;
+						}
+					});
+					if (file === null) break;
+					http_reply.header['Content-Type'] = 'application/octet-stream';
+					http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
+					http_reply.header['Content-Encoding'] = 'binary';
+					http_reply.header['Content-Length'] = file_size(file.path);
+					var f = new File(file.path);
+					f.open('rb');
+					for (var n = 0; n < f.length; n += CHUNK_SIZE) {
+						var r = f.length - f.position;
+						write(f.read(r > CHUNK_SIZE ? CHUNK_SIZE : r));
+						yield(false);
 					}
-				});
-				if (file === null) break;
-				http_reply.header['Content-Type'] = 'application/octet-stream';
-				http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
-				http_reply.header['Content-Encoding'] = 'binary';
-				http_reply.header['Content-Length'] = file_size(file.path);
-				var f = new File(file.path);
-				f.open('rb');
-				for (var n = 0; n < f.length; n += CHUNK_SIZE) {
-					var r = f.length - f.position;
-					write(f.read(r > CHUNK_SIZE ? CHUNK_SIZE : r));
-					yield(false);
+					f.close();
+					reply = false;
 				}
-				f.close();
-				reply = false;
-			}
-			break;
-		default:
-			break;
+				break;
+			default:
+				break;
+		}
+
 	}
 
-}
+	if (!reply) exit();
 
-if (!reply) exit();
+	reply = JSON.stringify(reply);
+	http_reply.header['Content-Type'] = 'application/json';
+	http_reply.header['Content-Length'] = reply.length;
+	write(reply);
 
-reply = JSON.stringify(reply);
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = reply.length;
-write(reply);
+})();
\ No newline at end of file
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index e6e8a7939e..7b4f5f6069 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -1,256 +1,258 @@
-/*  This script is an interface between HTTP clients and the functions defined
-    in web/lib/forum.js.  A basic check for an authenticated, non-guest user
-    is done here; otherwise all permission checking is done at the function
-    level. */
-
-var settings = load('modopts.js', 'web');
-
-require(settings.web_directory + '/lib/init.js', 'WEBV4_INIT');
-const auth_lib = load({}, settings.web_lib + 'auth.js');
-const forum_lib = load({}, settings.web_lib + 'forum.js');
-
-var reply = {};
-
-// There must be an API call, and the user must not be a guest or unknown
-if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-    typeof http_request.query.call !== 'undefined'
-) {
-
-    var handled = false;
-
-    // Authenticated calls
-    if (user.number > 0 && user.alias !== settings.guest) {
-
-        handled = true;
-
-        switch(http_request.query.call[0].toLowerCase()) {
-
-            case 'get-mail-unread-count':
-                reply.count = user.stats.mail_waiting;
-                break;
-
-            case 'get-mail-body':
-                if (typeof http_request.query.number !== 'undefined') {
-                    reply = forum_lib.getMailBody(http_request.query.number[0]);
-                }
-                break;
-
-            case 'get-signature':
-                reply.signature = forum_lib.getSignature();
-                break;
-
-            case 'post-reply':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.body !== 'undefined' &&
-                    typeof http_request.query.pid !== 'undefined'
-                ) {
-                    reply.success = forum_lib.postReply(
-                        http_request.query.sub[0],
-                        http_request.query.body[0],
-                        Number(http_request.query.pid[0])
-                    );
-                } else {
-                    reply.success = false;
-                }
-                break;
-
-            case 'post':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.to !== 'undefined' &&
-                    typeof http_request.query.subject !== 'undefined' &&
-                    typeof http_request.query.body !== 'undefined'
-                ) {
-                    reply.success = forum_lib.postNew(
-                        http_request.query.sub[0],
-                        http_request.query.to[0],
-                        http_request.query.subject[0],
-                        http_request.query.body[0]
-                    );
-                } else {
-                    reply.success = false;
-                }
-                break;
-
-            case 'delete-message':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.number !== 'undefined'
-                ) {
-                    reply.success = forum_lib.deleteMessage(
-                        http_request.query.sub[0],
-                        http_request.query.number[0]
-                    );
-                } else {
-                    reply.success = false;
-                }
-                break;
-
-            case 'delete-mail':
-                if (typeof http_request.query.number !== 'undefined') {
-                    reply.success = forum_lib.deleteMail(http_request.query.number);
-                } else {
-                    reply.success = false;
-                }
-                break;
-
-            case 'set-scan-cfg':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.cfg !== 'undefined'
-                ) {
-                    reply.success = forum_lib.setScanCfg(
-                        http_request.query.sub[0],
-                        http_request.query.cfg[0]
-                    );
-                } else {
-                    reply.success = false;
-                }
-                break;
-
-            case 'vote':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.id !== 'undefined' &&
-                    typeof http_request.query.up !== 'undefined' &&
-                    !(user.security.restrictions&UFLAG_V)
-                ) {
-                    reply.success = forum_lib.voteMessage(
-                        http_request.query.sub[0],
-                        http_request.query.id[0],
-                        http_request.query.up[0]
-                    );
-                } else {
-                    reply.success = false;
-                }
-                break;
-
-            case 'submit-poll-answers':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.id !== 'undefined' &&
-                    typeof http_request.query.answer !== 'undefined'
-                ) {
-                    reply.success = forum_lib.submitPollAnswers(
-                        http_request.query.sub[0],
-                        http_request.query.id[0],
-                        http_request.query.answer
-                    );
-                }
-                break;
-
-            case 'submit-poll':
-                if (typeof http_request.query.subject !== 'undefined' &&
-                    typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.votes !== 'undefined' &&
-                    typeof http_request.query.results !== 'undefined' &&
-                    typeof http_request.query.answer !== 'undefined'
-                ) {
-                    reply.success = forum_lib.postPoll(
-                        http_request.query.sub[0],
-                        http_request.query.subject[0],
-                        http_request.query.votes[0],
-                        http_request.query.results[0],
-                        http_request.query.answer,
-                        http_request.query.comment || []
-                    );
-                }
-                break;
-
-            default:
-                handled = false;
-                break;
+(function () {
+    /*  This script is an interface between HTTP clients and the functions defined
+        in web/lib/forum.js.  A basic check for an authenticated, non-guest user
+        is done here; otherwise all permission checking is done at the function
+        level. */
 
-        }
+    const settings = load('modopts.js', 'web');
 
-    }
+    load(settings.web_directory + '/lib/init.js');
+    load(settings.web_lib + 'auth.js');
+    load(settings.web_lib + 'forum.js');
+
+    var reply = {};
+
+    // There must be an API call, and the user must not be a guest or unknown
+    if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+        typeof http_request.query.call !== 'undefined'
+    ) {
+
+        var handled = false;
+
+        // Authenticated calls
+        if (user.number > 0 && user.alias !== settings.guest) {
+
+            handled = true;
+
+            switch(http_request.query.call[0].toLowerCase()) {
+
+                case 'get-mail-unread-count':
+                    reply.count = user.stats.mail_waiting;
+                    break;
+
+                case 'get-mail-body':
+                    if (typeof http_request.query.number !== 'undefined') {
+                        reply = getMailBody(http_request.query.number[0]);
+                    }
+                    break;
+
+                case 'get-signature':
+                    reply.signature = getSignature();
+                    break;
+
+                case 'post-reply':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.body !== 'undefined' &&
+                        typeof http_request.query.pid !== 'undefined'
+                    ) {
+                        reply.success = postReply(
+                            http_request.query.sub[0],
+                            http_request.query.body[0],
+                            Number(http_request.query.pid[0])
+                        );
+                    } else {
+                        reply.success = false;
+                    }
+                    break;
+
+                case 'post':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.to !== 'undefined' &&
+                        typeof http_request.query.subject !== 'undefined' &&
+                        typeof http_request.query.body !== 'undefined'
+                    ) {
+                        reply.success = postNew(
+                            http_request.query.sub[0],
+                            http_request.query.to[0],
+                            http_request.query.subject[0],
+                            http_request.query.body[0]
+                        );
+                    } else {
+                        reply.success = false;
+                    }
+                    break;
 
-    // Unauthenticated calls
-    if (!handled) {
+                case 'delete-message':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.number !== 'undefined'
+                    ) {
+                        reply.success = deleteMessage(
+                            http_request.query.sub[0],
+                            http_request.query.number[0]
+                        );
+                    } else {
+                        reply.success = false;
+                    }
+                    break;
 
-        switch(http_request.query.call[0].toLowerCase()) {
+                case 'delete-mail':
+                    if (typeof http_request.query.number !== 'undefined') {
+                        reply.success = deleteMail(http_request.query.number);
+                    } else {
+                        reply.success = false;
+                    }
+                    break;
 
-            case 'get-thread-votes':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.id !== 'undefined'
-                ) {
-                    var id = parseInt(http_request.query.id[0]);
-                    if (!isNaN(id)) {
-                        reply = forum_lib.getVotesInThread(
+                case 'set-scan-cfg':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.cfg !== 'undefined'
+                    ) {
+                        reply.success = setScanCfg(
+                            http_request.query.sub[0],
+                            http_request.query.cfg[0]
+                        );
+                    } else {
+                        reply.success = false;
+                    }
+                    break;
+
+                case 'vote':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.id !== 'undefined' &&
+                        typeof http_request.query.up !== 'undefined' &&
+                        !(user.security.restrictions&UFLAG_V)
+                    ) {
+                        reply.success = voteMessage(
+                            http_request.query.sub[0],
+                            http_request.query.id[0],
+                            http_request.query.up[0]
+                        );
+                    } else {
+                        reply.success = false;
+                    }
+                    break;
+
+                case 'submit-poll-answers':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.id !== 'undefined' &&
+                        typeof http_request.query.answer !== 'undefined'
+                    ) {
+                        reply.success = submitPollAnswers(
                             http_request.query.sub[0],
-                            id
+                            http_request.query.id[0],
+                            http_request.query.answer
                         );
                     }
-                }
-                break;
-
-            case 'get-sub-votes':
-                if (typeof http_request.query.sub !== 'undefined') {
-                    reply = forum_lib.getVotesInThreads(http_request.query.sub[0]);
-                }
-                break;
-
-            case 'get-poll-results':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.id !== 'undefined'
-                ) {
-                    reply = forum_lib.getUserPollData(
-                        http_request.query.sub[0],
-                        http_request.query.id[0]
-                    );
-                }
-                break;
-
-            case 'list-groups':
-                reply = forum_lib.listGroups();
-                break;
-
-            case 'list-subs':
-                if (typeof http_request.query.group !== 'undefined') {
-                    reply = forum_lib.listSubs(http_request.query.group[0]);
-                }
-                break;
-
-            case 'list-threads':
-                if (typeof http_request.query.sub !== 'undefined' &&
-                    typeof http_request.query.offset !== 'undefined'
-                ) {
-                    if (typeof http_request.query.count !== 'undefined') {
-                        var count = http_request.query.count[0];
+                    break;
+
+                case 'submit-poll':
+                    if (typeof http_request.query.subject !== 'undefined' &&
+                        typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.votes !== 'undefined' &&
+                        typeof http_request.query.results !== 'undefined' &&
+                        typeof http_request.query.answer !== 'undefined'
+                    ) {
+                        reply.success = postPoll(
+                            http_request.query.sub[0],
+                            http_request.query.subject[0],
+                            http_request.query.votes[0],
+                            http_request.query.results[0],
+                            http_request.query.answer,
+                            http_request.query.comment || []
+                        );
                     }
-                    reply = forum_lib.listThreads(
-                        http_request.query.sub[0],
-                        http_request.query.offset[0],
-                        count || settings.page_size
-                    ).threads;
-                }
-                break;
-
-            case 'get-group-unread-count':
-                if (typeof http_request.query.group !== 'undefined') {
-                    http_request.query.group.forEach(
-                        function(group) {
-                            reply[group] = forum_lib.getGroupUnreadCount(group);
+                    break;
+
+                default:
+                    handled = false;
+                    break;
+
+            }
+
+        }
+
+        // Unauthenticated calls
+        if (!handled) {
+
+            switch(http_request.query.call[0].toLowerCase()) {
+
+                case 'get-thread-votes':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.id !== 'undefined'
+                    ) {
+                        var id = parseInt(http_request.query.id[0]);
+                        if (!isNaN(id)) {
+                            reply = getVotesInThread(
+                                http_request.query.sub[0],
+                                id
+                            );
                         }
-                    );
-                }
-                break;
-
-            case 'get-sub-unread-count':
-                if (typeof http_request.query.sub !== 'undefined') {
-                    http_request.query.sub.forEach(
-                        function(sub) {
-                            reply[sub] = forum_lib.getSubUnreadCount(sub);
+                    }
+                    break;
+
+                case 'get-sub-votes':
+                    if (typeof http_request.query.sub !== 'undefined') {
+                        reply = getVotesInThreads(http_request.query.sub[0]);
+                    }
+                    break;
+
+                case 'get-poll-results':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.id !== 'undefined'
+                    ) {
+                        reply = getUserPollData(
+                            http_request.query.sub[0],
+                            http_request.query.id[0]
+                        );
+                    }
+                    break;
+
+                case 'list-groups':
+                    reply = listGroups();
+                    break;
+
+                case 'list-subs':
+                    if (typeof http_request.query.group !== 'undefined') {
+                        reply = listSubs(http_request.query.group[0]);
+                    }
+                    break;
+
+                case 'list-threads':
+                    if (typeof http_request.query.sub !== 'undefined' &&
+                        typeof http_request.query.offset !== 'undefined'
+                    ) {
+                        if (typeof http_request.query.count !== 'undefined') {
+                            var count = http_request.query.count[0];
                         }
-                    );
-                }
-                break;
+                        reply = listThreads(
+                            http_request.query.sub[0],
+                            http_request.query.offset[0],
+                            count || settings.page_size
+                        ).threads;
+                    }
+                    break;
+
+                case 'get-group-unread-count':
+                    if (typeof http_request.query.group !== 'undefined') {
+                        http_request.query.group.forEach(
+                            function(group) {
+                                reply[group] = getGroupUnreadCount(group);
+                            }
+                        );
+                    }
+                    break;
+
+                case 'get-sub-unread-count':
+                    if (typeof http_request.query.sub !== 'undefined') {
+                        http_request.query.sub.forEach(
+                            function(sub) {
+                                reply[sub] = getSubUnreadCount(sub);
+                            }
+                        );
+                    }
+                    break;
+
+                default:
+                    break;
 
-            default:
-                break;
+            }
 
         }
 
     }
 
-}
-
-reply = JSON.stringify(reply);
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = reply.length;
-write(reply);
+    reply = JSON.stringify(reply);
+    http_reply.header['Content-Type'] = 'application/json';
+    http_reply.header['Content-Length'] = reply.length;
+    write(reply);
+})();
\ No newline at end of file
diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 460bca8582..1e414a6bb4 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,83 +1,85 @@
-/*	Posts a notification message to a list of sub-boards when commits are made
-	to a GitHub repository.
+(function () {
+	/*	Posts a notification message to a list of sub-boards when commits are made
+		to a GitHub repository.
 
-	- In your repository, go to Settings -> Webhooks -> Add webhook
-	- Payload URL: https://your-bbs-hostname/api/github.ssjs
-	- Content-type: application/json
-	- Secret: some-secret-passphrase-here
-	- Which events: Just the push event
-	- Active: checked
-	- Add webhook
+		- In your repository, go to Settings -> Webhooks -> Add webhook
+		- Payload URL: https://your-bbs-hostname/api/github.ssjs
+		- Content-type: application/json
+		- Secret: some-secret-passphrase-here
+		- Which events: Just the push event
+		- Active: checked
+		- Add webhook
 
-	- On your BBS server, edit ctrl/modopts.ini
-	- Add a [github-notify] section
-	- For each repository you wish to configure, add a key-value pair like:
-	- my-repository-name=secret,SUB
-	- Where 'secret' is the secret passphrase as configured above
-	- Where 'SUB' is the internal code of a sub-board to post to
-	- Multiple subs can be specified, separated by commas
-*/
+		- On your BBS server, edit ctrl/modopts.ini
+		- Add a [github-notify] section
+		- For each repository you wish to configure, add a key-value pair like:
+		- my-repository-name=secret,SUB
+		- Where 'secret' is the secret passphrase as configured above
+		- Where 'SUB' is the internal code of a sub-board to post to
+		- Multiple subs can be specified, separated by commas
+	*/
 
-load('sbbsdefs.js');
-load('hmac.js');
-const options = load({}, 'modopts.js', 'github_notify');
-load(system.exec_dir + '../web/lib/init.js');
+	load('sbbsdefs.js');
+	load('hmac.js');
+	const options = load({}, 'modopts.js', 'github_notify');
+	load(system.exec_dir + '../web/lib/init.js');
 
-function b2h(str) {
-	return str.split('').map(function (e) {
-		var n = ascii(e).toString(16);
-		return n.length < 2 ? ('0' + n) : n;
-	}).join('');
-}
-
-function verify_signature(key, payload, hash) {
-	return (b2h(hmac_sha1(key, payload)) === hash);
-}
-
-try {
-	const hash = http_request.header['x-hub-signature'].split('=')[1];
-	const payload = JSON.parse(http_request.post_data);
-	if (typeof options[payload.repository.name] === 'undefined') {
-		throw 'Unknown repository ' + payload.repository.name;
-	}
-	const subs = options[payload.repository.name].split(',');
-	const secret = subs.shift();
-	if (!verify_signature(secret, http_request.post_data, hash)) {
-		throw 'GitHub signature mismatch';
+	function b2h(str) {
+		return str.split('').map(function (e) {
+			var n = ascii(e).toString(16);
+			return n.length < 2 ? ('0' + n) : n;
+		}).join('');
 	}
-} catch (err) {
-	log(LOG_ERR, err);
-	exit();
-}
 
-const header = {
-	from: payload.head_commit.author.username,
-	to: 'All',
-	subject: 'Changes to ' + payload.repository.full_name
-};
-
-const body = payload.commits.map(function (e) {
-	const ret = [ 'Commit ID: ' + e.id, 'Author: ' + e.author.username ];
-	if (e.added.length) ret.push('Added: ' + e.added.join(', '));
-	if (e.removed.length) ret.push('Removed: ' + e.removed.join(', '));
-	if (e.modified.length) ret.push('Modified: ' + e.modified.join(', '));
-	ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
-	return ret.join('\r\n');
-}).concat('Repository URL: ' + payload.repository.url).join('\r\n\r\n');
+	function verify_signature(key, payload, hash) {
+		return (b2h(hmac_sha1(key, payload)) === hash);
+	}
 
-subs.forEach(function (sub) {
 	try {
-		const mb = new MsgBase(sub);
-		if (!mb.open()) throw 'Failed to open message base ' + sub;
-		if (!mb.save_msg(header, body)) throw 'Failed to save message';
-		mb.close();
+		const hash = http_request.header['x-hub-signature'].split('=')[1];
+		const payload = JSON.parse(http_request.post_data);
+		if (typeof options[payload.repository.name] === 'undefined') {
+			throw 'Unknown repository ' + payload.repository.name;
+		}
+		const subs = options[payload.repository.name].split(',');
+		const secret = subs.shift();
+		if (!verify_signature(secret, http_request.post_data, hash)) {
+			throw 'GitHub signature mismatch';
+		}
 	} catch (err) {
 		log(LOG_ERR, err);
+		exit();
 	}
-});
 
-const _tf = new File(system.data_dir + '/github_notify');
-if (_tf.open('w')) {
-    _tf.write(body);
-    _tf.close();
-}
+	const header = {
+		from: payload.head_commit.author.username,
+		to: 'All',
+		subject: 'Changes to ' + payload.repository.full_name
+	};
+
+	const body = payload.commits.map(function (e) {
+		const ret = [ 'Commit ID: ' + e.id, 'Author: ' + e.author.username ];
+		if (e.added.length) ret.push('Added: ' + e.added.join(', '));
+		if (e.removed.length) ret.push('Removed: ' + e.removed.join(', '));
+		if (e.modified.length) ret.push('Modified: ' + e.modified.join(', '));
+		ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
+		return ret.join('\r\n');
+	}).concat('Repository URL: ' + payload.repository.url).join('\r\n\r\n');
+
+	subs.forEach(function (sub) {
+		try {
+			const mb = new MsgBase(sub);
+			if (!mb.open()) throw 'Failed to open message base ' + sub;
+			if (!mb.save_msg(header, body)) throw 'Failed to save message';
+			mb.close();
+		} catch (err) {
+			log(LOG_ERR, err);
+		}
+	});
+
+	const _tf = new File(system.data_dir + '/github_notify');
+	if (_tf.open('w')) {
+		_tf.write(body);
+		_tf.close();
+	}
+})();
\ No newline at end of file
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 58178b73e8..74ce4388a8 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,219 +1,221 @@
-load('sbbsdefs.js');
-load(system.exec_dir + '../web/lib/init.js');
-load(settings.web_lib + '/auth.js');
-
-if (user.alias !== settings.guest) exit();
-if (!settings.user_registration) exit();
-
-var MIN_ALIAS = 1,
-	MIN_REALNAME = 3,
-	MIN_NETMAIL = 6,
-	MIN_LOCATION = 4,
-	MIN_ADDRESS = 6,
-	MIN_PHONE = 3;
-
-var reply = {
-	errors : [],
-	userNumber : 0
-};
-
-var prepUser = {
-	alias : '',
-	handle : '',
-	name : '',
-	netmail : '',
-	address : '',
-	zipcode : '',
-	location : '',
-	phone : '',
-	birthdate : '',
-	gender : '',
-	password : ''
-};
-
-function required(mask) {
-	return (system.new_user_questions&mask);
-}
-
-function cleanParam(param) {
-	if (paramExists(param)) {
-		return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
-	}
-	return "";
-}
-
-function paramExists(param) {
-	if (typeof http_request.query[param] !== 'undefined' &&
-		http_request.query[param][0] !== ''
+(function () {
+	load('sbbsdefs.js');
+	load(system.exec_dir + '../web/lib/init.js');
+	load(settings.web_lib + '/auth.js');
+
+	if (user.alias !== settings.guest) exit();
+	if (!settings.user_registration) exit();
+
+	var MIN_ALIAS = 1,
+		MIN_REALNAME = 3,
+		MIN_NETMAIL = 6,
+		MIN_LOCATION = 4,
+		MIN_ADDRESS = 6,
+		MIN_PHONE = 3;
+
+	var reply = {
+		errors : [],
+		userNumber : 0
+	};
+
+	var prepUser = {
+		alias : '',
+		handle : '',
+		name : '',
+		netmail : '',
+		address : '',
+		zipcode : '',
+		location : '',
+		phone : '',
+		birthdate : '',
+		gender : '',
+		password : ''
+	};
+
+	function required(mask) {
+		return (system.new_user_questions&mask);
+	}
+
+	function cleanParam(param) {
+		if (paramExists(param)) {
+			return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
+		}
+		return "";
+	}
+
+	function paramExists(param) {
+		if (typeof http_request.query[param] !== 'undefined' &&
+			http_request.query[param][0] !== ''
+		) {
+			return true;
+		}
+		return false;
+	}
+
+	function paramLength(param) {
+		if (typeof http_request.query[param] === 'undefined') {
+			return 0;
+		} else if (http_request.query[param][0].replace(' ', '').length < 1) {
+			return 0;
+		} else if (cleanParam(param).length < 1) {
+			return 0;
+		} else {
+			return http_request.query[param][0].length;
+		}
+	}
+
+	function newUser() {
+		var usr = system.new_user(prepUser.alias);
+		if (typeof usr === 'number') {
+			reply.errors.push(locale.strings.api_register.error_failed);
+			return;
+		}
+		log(LOG_INFO, format(locale.strings.api_register.log_success, usr.number));
+		usr.security.password = prepUser.password;
+		for (var property in prepUser) {
+			if (property === 'alias' || property === 'password') continue;
+			usr[property] = prepUser[property];
+		}
+		reply.userNumber = usr.number;
+	}
+
+	// See if the hidden form fields were filled
+	if ((	paramExists('send-me-free-stuff') &&
+			http_request.query['send-me-free-stuff'][0] !== ''
+		) ||
+		(	paramExists('subscribe-to-newsletter') &&
+			http_request.query['subscribe-to-newsletter'][0] !== ''
+		)
+	) {
+		log(LOG_WARNING, locale.strings.api_register.log_bot_attempt);
+		exit();
+	}
+
+	if (system.newuser_password !== '' &&
+		(	typeof http_request.query['newuser-password'] === 'undefined' ||
+			http_request.query['newuser-password'][0] != system.newuser_password
+		)
+	) {
+		reply.errors.push(locale.strings.api_register.error_bad_syspass);
+	}
+
+	// More could be done to respect certain newuser question toggles
+	// (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
+
+	if (!paramExists('alias') ||
+		paramLength('alias') < MIN_ALIAS ||
+		paramLength('alias') > LEN_ALIAS
+	) {
+		reply.errors.push(locale.strings.api_register.error_invalid_alias);
+	} else if (system.matchuser(http_request.query.alias[0]) > 0) {
+		reply.errors.push(locale.strings.api_register.error_alias_taken);
+	} else {
+		prepUser.alias = cleanParam('alias');
+		prepUser.handle = cleanParam('alias');
+	}
+
+	if ((!paramExists('password1') || !paramExists('password2')) ||
+		http_request.query.password1[0] !== http_request.query.password2[0]
+	) {
+		reply.errors.push(locale.strings.api_register.error_password_mismatch);
+	} else if (
+		paramLength('password1') < settings.minimum_password_length ||
+		paramLength('password1') > LEN_PASS
+	) {
+		reply.errors.push(
+			format(
+				locale.strings.api_register.error_password_length,
+				settings.minimum_password_length, LEN_PASS
+			)
+		);
+	} else {
+		prepUser.password = cleanParam('password1');
+	}
+
+	if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
+		reply.errors.push(locale.strings.api_register.error_email_required);
+	} else if (
+		(	paramLength('netmail') < MIN_NETMAIL ||
+			paramLength('netmail') > LEN_NETMAIL
+		) && !required(UQ_NONETMAIL)
+	) {
+		reply.errors.push(locale.strings.api_register.error_invalid_email);
+	} else {
+		prepUser.netmail = cleanParam('netmail');
+	}
+
+	if (required(UQ_REALNAME) &&
+		(	!paramExists('realname') ||
+			paramLength('realname') < MIN_REALNAME ||
+			paramLength('realname') > LEN_NAME
+		)
 	) {
-		return true;
-	}
-	return false;
-}
-
-function paramLength(param) {
-	if (typeof http_request.query[param] === 'undefined') {
-		return 0;
-	} else if (http_request.query[param][0].replace(' ', '').length < 1) {
-		return 0;
-	} else if (cleanParam(param).length < 1) {
-		return 0;
+		reply.errors.push(locale.strings.api_register.error_invalid_name);
 	} else {
-		return http_request.query[param][0].length;
-	}
-}
-
-function newUser() {
-	var usr = system.new_user(prepUser.alias);
-	if (typeof usr === 'number') {
-		reply.errors.push(locale.strings.api_register.error_failed);
-		return;
-	}
-	log(LOG_INFO, format(locale.strings.api_register.log_success, usr.number));
-	usr.security.password = prepUser.password;
-	for (var property in prepUser) {
-		if (property === 'alias' || property === 'password') continue;
-		usr[property] = prepUser[property];
-	}
-	reply.userNumber = usr.number;
-}
-
-// See if the hidden form fields were filled
-if ((	paramExists('send-me-free-stuff') &&
-		http_request.query['send-me-free-stuff'][0] !== ''
-	) ||
-	(	paramExists('subscribe-to-newsletter') &&
-		http_request.query['subscribe-to-newsletter'][0] !== ''
-	)
-) {
-	log(LOG_WARNING, locale.strings.api_register.log_bot_attempt);
-	exit();
-}
-
-if (system.newuser_password !== '' &&
-	(	typeof http_request.query['newuser-password'] === 'undefined' ||
-		http_request.query['newuser-password'][0] != system.newuser_password
-	)
-) {
-	reply.errors.push(locale.strings.api_register.error_bad_syspass);
-}
-
-// More could be done to respect certain newuser question toggles
-// (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
-
-if (!paramExists('alias') ||
-	paramLength('alias') < MIN_ALIAS ||
-	paramLength('alias') > LEN_ALIAS
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_alias);
-} else if (system.matchuser(http_request.query.alias[0]) > 0) {
-	reply.errors.push(locale.strings.api_register.error_alias_taken);
-} else {
-	prepUser.alias = cleanParam('alias');
-	prepUser.handle = cleanParam('alias');
-}
-
-if ((!paramExists('password1') || !paramExists('password2')) ||
-	http_request.query.password1[0] !== http_request.query.password2[0]
-) {
-	reply.errors.push(locale.strings.api_register.error_password_mismatch);
-} else if (
-	paramLength('password1') < settings.minimum_password_length ||
-	paramLength('password1') > LEN_PASS
-) {
-	reply.errors.push(
-		format(
-			locale.strings.api_register.error_password_length,
-			settings.minimum_password_length, LEN_PASS
+		prepUser.name = cleanParam('realname');
+	}
+
+	if (required(UQ_LOCATION) &&
+		(	!paramExists('location') ||
+			paramLength('location') < MIN_LOCATION ||
+			paramLength('location') > LEN_LOCATION
 		)
-	);
-} else {
-	prepUser.password = cleanParam('password1');
-}
-
-if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
-	reply.errors.push(locale.strings.api_register.error_email_required);
-} else if (
-	(	paramLength('netmail') < MIN_NETMAIL ||
-		paramLength('netmail') > LEN_NETMAIL
-	) && !required(UQ_NONETMAIL)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_email);
-} else {
-	prepUser.netmail = cleanParam('netmail');
-}
-
-if (required(UQ_REALNAME) &&
-	(	!paramExists('realname') ||
-		paramLength('realname') < MIN_REALNAME ||
-		paramLength('realname') > LEN_NAME
-	)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_name);
-} else {
-	prepUser.name = cleanParam('realname');
-}
-
-if (required(UQ_LOCATION) &&
-	(	!paramExists('location') ||
-		paramLength('location') < MIN_LOCATION ||
-		paramLength('location') > LEN_LOCATION
-	)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_location);
-} else {
-	prepUser.location = cleanParam('location');
-}
-
-if (required(UQ_ADDRESS) &&
-	(	!paramExists('address') ||
-		paramLength('address') < MIN_ADDRESS ||
-		paramLength('address') > LEN_ADDRESS ||
-		!paramExists('zipcode') ||
-		paramLength('zipcode') < 3 ||
-		paramLength('zipcode') > LEN_ADDRESS
-	)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_street_address);
-} else {
-	prepUser.address = cleanParam('address');
-	prepUser.zipcode = cleanParam('zipcode');
-}
-
-if (required(UQ_PHONE) &&
-	(	!paramExists('phone') ||
-		paramLength('phone') < MIN_PHONE ||
-		paramLength('phone') > LEN_PHONE
-	)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_phone);
-} else {
-	prepUser.phone = cleanParam('phone');
-}
-
-if (required(UQ_SEX) &&
-	(	!paramExists('gender') ||
-		paramLength('gender') != 1 ||
-		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
-	)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_gender);
-} else {
-	prepUser.gender = http_request.query.gender[0];
-}
-
-if (paramExists('birth') &&
-	http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
-) {
-	// Should really test for valid date (and date format per system config)
-	prepUser.birthdate = cleanParam('birth');
-} else if (required(UQ_BIRTH)) {
-	reply.errors.push(locale.strings.api_register.error_invalid_birthdate);
-}
-
-if (reply.errors.length < 1) newUser();
-
-reply = JSON.stringify(reply);
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = reply.length;
-write(reply);
+	) {
+		reply.errors.push(locale.strings.api_register.error_invalid_location);
+	} else {
+		prepUser.location = cleanParam('location');
+	}
+
+	if (required(UQ_ADDRESS) &&
+		(	!paramExists('address') ||
+			paramLength('address') < MIN_ADDRESS ||
+			paramLength('address') > LEN_ADDRESS ||
+			!paramExists('zipcode') ||
+			paramLength('zipcode') < 3 ||
+			paramLength('zipcode') > LEN_ADDRESS
+		)
+	) {
+		reply.errors.push(locale.strings.api_register.error_invalid_street_address);
+	} else {
+		prepUser.address = cleanParam('address');
+		prepUser.zipcode = cleanParam('zipcode');
+	}
+
+	if (required(UQ_PHONE) &&
+		(	!paramExists('phone') ||
+			paramLength('phone') < MIN_PHONE ||
+			paramLength('phone') > LEN_PHONE
+		)
+	) {
+		reply.errors.push(locale.strings.api_register.error_invalid_phone);
+	} else {
+		prepUser.phone = cleanParam('phone');
+	}
+
+	if (required(UQ_SEX) &&
+		(	!paramExists('gender') ||
+			paramLength('gender') != 1 ||
+			['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
+		)
+	) {
+		reply.errors.push(locale.strings.api_register.error_invalid_gender);
+	} else {
+		prepUser.gender = http_request.query.gender[0];
+	}
+
+	if (paramExists('birth') &&
+		http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
+	) {
+		// Should really test for valid date (and date format per system config)
+		prepUser.birthdate = cleanParam('birth');
+	} else if (required(UQ_BIRTH)) {
+		reply.errors.push(locale.strings.api_register.error_invalid_birthdate);
+	}
+
+	if (reply.errors.length < 1) newUser();
+
+	reply = JSON.stringify(reply);
+	http_reply.header['Content-Type'] = 'application/json';
+	http_reply.header['Content-Length'] = reply.length;
+	write(reply);
+})();
\ No newline at end of file
diff --git a/web/root/api/sbbsimsg.ssjs b/web/root/api/sbbsimsg.ssjs
index 976e45f896..510b17e91d 100644
--- a/web/root/api/sbbsimsg.ssjs
+++ b/web/root/api/sbbsimsg.ssjs
@@ -1,28 +1,29 @@
-load("sbbsdefs.js");
-load("nodedefs.js");
-load('modopts.js');
-var settings = get_mod_options('web');
-load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
-const sbbsimsg = load({}, "sbbsimsg_lib.js");
+(function () {
+    load("sbbsdefs.js");
+    load("nodedefs.js");
+    const settings = load('modopts.js', 'web');
+    load(settings.web_directory + '/lib/init.js');
+    load(settings.web_lib + 'auth.js');
+    const sbbsimsg = load({}, "sbbsimsg_lib.js");
 
-var reply = {};
+    var reply = {};
 
-if (user.number > 0 && user.alias != settings.guest
-    && typeof http_request.query.call != 'undefined'
-    && http_request.query.call[0] == 'send_telegram'
-    && typeof http_request.query.host != 'undefined'
-    && typeof http_request.query.username != 'undefined'
-    && typeof http_request.query.message != 'undefined'
-) {
-    sbbsimsg.send_msg(
-        http_request.query.username[0] + '@' + http_request.query.host[0],
-        http_request.query.message[0],
-        user.alias
-    );
-}
+    if (user.number > 0 && user.alias != settings.guest
+        && typeof http_request.query.call != 'undefined'
+        && http_request.query.call[0] == 'send_telegram'
+        && typeof http_request.query.host != 'undefined'
+        && typeof http_request.query.username != 'undefined'
+        && typeof http_request.query.message != 'undefined'
+    ) {
+        sbbsimsg.send_msg(
+            http_request.query.username[0] + '@' + http_request.query.host[0],
+            http_request.query.message[0],
+            user.alias
+        );
+    }
 
-reply = JSON.stringify(reply);
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = reply.length;
-write(reply);
+    reply = JSON.stringify(reply);
+    http_reply.header['Content-Type'] = 'application/json';
+    http_reply.header['Content-Length'] = reply.length;
+    write(reply);
+})();
\ No newline at end of file
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index d099c8a0eb..be1103303e 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -1,98 +1,99 @@
-load('sbbsdefs.js');
-load('nodedefs.js');
-load('modopts.js');
-var settings = get_mod_options('web');
+(function () {
+	load('sbbsdefs.js');
+	load('nodedefs.js');
+	const settings = load('modopts.js', 'web');
 
-load(settings.web_directory + '/lib/init.js');
-load(settings.web_lib + 'auth.js');
+	load(settings.web_directory + '/lib/init.js');
+	load(settings.web_lib + 'auth.js');
 
-var reply = {};
+	var reply = {};
 
-if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-	typeof http_request.query.call !== 'undefined' &&
-	user.number > 0
-) {
+	if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+		typeof http_request.query.call !== 'undefined' &&
+		user.number > 0
+	) {
 
-	switch (http_request.query.call[0]) {
+		switch (http_request.query.call[0]) {
 
-		case 'node-list':
-            var usr = new User(1);
-			reply = system.node_list.reduce(function (a, c, i) {
-                if (c.status !== 3) return a;
-                usr.number = c.useron;
-				a.push({
-                    node: i,
-                    status: format(NodeStatus[c.status], c.aux, c.extaux),
-					action: format(NodeAction[c.action], c.aux, c.extaux),
-                    user: usr.alias,
-                    connection: usr.connection
-				});
-                return a;
-			}, []);
-			for (var un = 1; un < system.lastuser; un++) {
-				usr.number = un;
-				if (usr.connection !== 'HTTP') continue;
-				if (usr.alias === settings.guest) continue;
-				if (usr.settings&USER_QUIET) continue;
-				if (usr.logontime < time() - settings.inactivity) continue;
-				var webAction = auth_lib.getSessionValue(usr.number, 'action');
-				if (webAction === null) continue;
-				reply.push({
-                    status: '',
-					action: locale.strings.api_system.nodelist_action_prefix + ' ' + webAction,
-					user: usr.alias,
-                    connection: 'W'
-				});
-			}
-            usr = undefined;
-			break;
+			case 'node-list':
+				var usr = new User(1);
+				reply = system.node_list.reduce(function (a, c, i) {
+					if (c.status !== 3) return a;
+					usr.number = c.useron;
+					a.push({
+						node: i,
+						status: format(NodeStatus[c.status], c.aux, c.extaux),
+						action: format(NodeAction[c.action], c.aux, c.extaux),
+						user: usr.alias,
+						connection: usr.connection
+					});
+					return a;
+				}, []);
+				for (var un = 1; un < system.lastuser; un++) {
+					usr.number = un;
+					if (usr.connection !== 'HTTP') continue;
+					if (usr.alias === settings.guest) continue;
+					if (usr.settings&USER_QUIET) continue;
+					if (usr.logontime < time() - settings.inactivity) continue;
+					var webAction = auth_lib.getSessionValue(usr.number, 'action');
+					if (webAction === null) continue;
+					reply.push({
+						status: '',
+						action: locale.strings.api_system.nodelist_action_prefix + ' ' + webAction,
+						user: usr.alias,
+						connection: 'W'
+					});
+				}
+				usr = undefined;
+				break;
 
-		case 'send-telegram':
-			if (user.alias === settings.guest) break;
-			if (typeof http_request.query.user === 'undefined') break;
-			if (typeof http_request.query.telegram === 'undefined' ||
-				http_request.query.telegram[0] === ''
-			) {
+			case 'send-telegram':
+				if (user.alias === settings.guest) break;
+				if (typeof http_request.query.user === 'undefined') break;
+				if (typeof http_request.query.telegram === 'undefined' ||
+					http_request.query.telegram[0] === ''
+				) {
+					break;
+				}
+				if (http_request.query.telegram[0].length >
+					settings.maximum_telegram_length
+				) {
+					break;
+				}
+				var un = system.matchuser(http_request.query.user[0]);
+				if (un < 1) break;
+				system.put_telegram(
+					un, format(
+						locale.strings.api_system.telegram_header_format,
+						user.alias, (new Date()).toLocaleString()
+					) + '\r\n' + http_request.query.telegram[0] + '\r\n'
+				);
 				break;
-			}
-			if (http_request.query.telegram[0].length >
-				settings.maximum_telegram_length
-			) {
+
+			case 'get-telegram':
+				if (user.alias === settings.guest) break;
+				reply.telegram = system.get_telegram(user.number);
 				break;
-			}
-			var un = system.matchuser(http_request.query.user[0]);
-			if (un < 1) break;
-			system.put_telegram(
-				un, format(
-					locale.strings.api_system.telegram_header_format,
-					user.alias, (new Date()).toLocaleString()
-				) + '\r\n' + http_request.query.telegram[0] + '\r\n'
-			);
-			break;
 
-		case 'get-telegram':
-			if (user.alias === settings.guest) break;
-			reply.telegram = system.get_telegram(user.number);
-			break;
+			case 'set-xtrn-intent':
+				if (user.alias === settings.guest) break;
+				if (typeof http_request.query.code === 'undefined') break;
+				if (http_request.query.code[0].length > 8) break;
+				if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
+					break;
+				}
+				auth_lib.setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
+				break;
 
-		case 'set-xtrn-intent':
-			if (user.alias === settings.guest) break;
-			if (typeof http_request.query.code === 'undefined') break;
-			if (http_request.query.code[0].length > 8) break;
-			if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
+			default:
 				break;
-			}
-			auth_lib.setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
-			break;
 
-		default:
-			break;
+		}
 
 	}
 
-}
-
-reply = JSON.stringify(reply);
-http_reply.header['Content-Type'] = 'application/json';
-http_reply.header['Content-Length'] = reply.length;
-write(reply);
+	reply = JSON.stringify(reply);
+	http_reply.header['Content-Type'] = 'application/json';
+	http_reply.header['Content-Length'] = reply.length;
+	write(reply);
+})();
\ No newline at end of file
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 49d63e7de0..2705bb4f0e 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,11 +1,12 @@
-<?xjs
-
-	var settings = load('modopts.js', 'web');
+<?xjs (function () { ?>
 
+<?xjs
+	const settings = load('modopts.js', 'web');
 	load(settings.web_directory + '/lib/init.js');
-	var auth_lib = load({}, settings.web_lib + 'auth.js');
-	var page_lib = load({}, settings.web_lib + 'pages.js');
-	var sidebar_lib = load({}, settings.web_lib + 'sidebar.js');
+
+	load(settings.web_lib + 'auth.js');
+	load(settings.web_lib + 'pages.js');
+	load(settings.web_lib + 'sidebar.js');
 
 	var page = typeof http_request.query.page == 'undefined' ? '000-home.xjs' : http_request.query.page[0];
     if (page.search(/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/i) > -1) {
@@ -17,8 +18,7 @@
     if (!file_exists(fullpath(settings.web_pages + page)) || fullpath(settings.web_pages + page).indexOf(fullpath(settings.web_pages)) !== 0) {
         page = '000-home.xjs';
     }
-	var page_ctrl = page_lib.getCtrlLine(settings.web_pages + page);
-
+	var page_ctrl = getCtrlLine(settings.web_pages + page);
 ?>
 
 <?xjs function _subMenu(obj, title, path) { ?>
@@ -55,7 +55,7 @@
 		if (settings.layout_sidebar_off || page_ctrl.options.no_sidebar) return;
 ?>
 	<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
-		<?xjs sidebar_lib.writeSidebarModules(); ?>
+		<?xjs writeSidebarModules(); ?>
 	</div>
 <?xjs } ?>
 
@@ -123,7 +123,7 @@
 				</div>
 				<div id="navbar" class="collapse navbar-collapse">
 					<ul class="nav navbar-nav">
-						<?xjs _menu(page_lib.getPageList(settings.web_pages)); ?>
+						<?xjs _menu(getPageList(settings.web_pages)); ?>
 					</ul>
 					<ul class="nav navbar-nav navbar-right">
 						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
@@ -189,7 +189,7 @@
 						<button title="Toggle sidebar" type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><span class="glyphicon glyphicon-tasks"></span><?xjs write(locale.strings.main.label_sidebar); ?></button>
 					</p>
 					</div>
-					<?xjs page_lib.writePage(page); ?>
+					<?xjs writePage(page); ?>
 				</div>
 				<?xjs if (!settings.layout_sidebar_left || settings.layout_sidebar_right) _sidebar(); ?>
 			</div>
@@ -217,3 +217,5 @@
 	</body>
 
 </html>
+
+<?xjs })(); ?>
\ No newline at end of file
-- 
GitLab


From e3f3799002c21eacbaee6998880142387d745474 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 29 Jul 2019 21:43:22 -0400
Subject: [PATCH 501/752] Remove closures.  They shouldn't be necessary, and
 anyway while they solve the unexpected redeclaration errors, other unexpected
 scope problems persist.

---
 web/root/api/auth.ssjs     |  20 +-
 web/root/api/events.ssjs   |  98 ++++----
 web/root/api/files.ssjs    | 108 ++++-----
 web/root/api/forum.ssjs    | 480 ++++++++++++++++++-------------------
 web/root/api/github.ssjs   | 144 ++++++-----
 web/root/api/register.ssjs | 434 +++++++++++++++++----------------
 web/root/api/sbbsimsg.ssjs |  51 ++--
 web/root/api/system.ssjs   | 166 +++++++------
 8 files changed, 741 insertions(+), 760 deletions(-)

diff --git a/web/root/api/auth.ssjs b/web/root/api/auth.ssjs
index 1e5dac3b90..f0454aea47 100644
--- a/web/root/api/auth.ssjs
+++ b/web/root/api/auth.ssjs
@@ -1,15 +1,13 @@
-(function () {
-	const settings = load('modopts.js', 'web');
+var settings = load('modopts.js', 'web');
 
-	load(settings.web_directory + '/lib/init.js');
-	load(settings.web_lib + 'auth.js');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
 
-	var response = JSON.stringify({
-		authenticated: (user.alias !== settings.guest)
-	});
+var response = JSON.stringify({
+	authenticated: (user.alias !== settings.guest)
+});
 
-	http_reply.header['Content-Type'] = 'application/json';
-	http_reply.header['Content-Length'] = response.length;
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = response.length;
 
-	write(response);
-})();
\ No newline at end of file
+write(response);
diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 07f4d78e76..d48bdd2e53 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,62 +1,58 @@
-//(function () {
-    
-    load('sbbsdefs.js');
-    const settings = load('modopts.js', 'web');
-    load(settings.web_directory + '/lib/init.js');
-    load(settings.web_lib + 'auth.js');
-    load(settings.web_lib + 'request.js');
+load('sbbsdefs.js');
+const settings = load('modopts.js', 'web');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'request.js');
 
-    js.time_limit = 0;
+js.time_limit = 0;
 
-    http_reply.header['Cache-Control'] = 'no-cache';
-    http_reply.header['Content-type'] = 'text/event-stream';
-    http_reply.header['X-Accel-Buffering'] = 'no'; // probably not needed by everyone (nginx)
+http_reply.header['Cache-Control'] = 'no-cache';
+http_reply.header['Content-type'] = 'text/event-stream';
+http_reply.header['X-Accel-Buffering'] = 'no'; // probably not needed by everyone (nginx)
 
-    const keepalive = 15;
-    var last_send = 0;
+const keepalive = 15;
+var last_send = 0;
 
-    function ping() {
-        if (time() - last_send > keepalive) {
-            write(': ping\n\n');
-            last_send = time();
-        }
-    }
-
-    function emit(obj) {
-        Object.keys(obj).forEach(function (e) {
-            write(e + ': ' + (typeof obj[e] == 'object' ? JSON.stringify(obj[e]) : obj[e]) + '\n');
-        });
-        write('\n');
+function ping() {
+    if (time() - last_send > keepalive) {
+        write(': ping\n\n');
         last_send = time();
     }
-
-    const callbacks = {};
-    if (file_isdir(settings.web_lib + 'events')) {
-        if (Array.isArray(http_request.query.subscribe)) {
-            http_request.query.subscribe.forEach(function (e) {
-                const base = file_getname(e).replace(file_getext(e), '');
-                const script = settings.web_lib + 'events/' + base + '.js';
-                try {
-                    if (file_exists(script)) callbacks[e] = load({}, script);
-                } catch (err) {
-                   log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
-                }
-            });
-        }
-    }
-
-    ping();
-    while (client.socket.is_connected) {
-        Object.keys(callbacks).forEach(function (e) {
+}
+
+function emit(obj) {
+    Object.keys(obj).forEach(function (e) {
+        write(e + ': ' + (typeof obj[e] == 'object' ? JSON.stringify(obj[e]) : obj[e]) + '\n');
+    });
+    write('\n');
+    last_send = time();
+}
+
+const callbacks = {};
+if (file_isdir(settings.web_lib + 'events')) {
+    if (Array.isArray(http_request.query.subscribe)) {
+        http_request.query.subscribe.forEach(function (e) {
+            const base = file_getname(e).replace(file_getext(e), '');
+            const script = settings.web_lib + 'events/' + base + '.js';
             try {
-                callbacks[e].cycle();
+                if (file_exists(script)) callbacks[e] = load({}, script);
             } catch (err) {
-               log(LOG_ERR, 'Callback ' + e + ' failed: ' + err);
-               delete callbacks[e];
+                log(LOG_ERR, 'Failed to load event module ' + e + ': ' + err);
             }
         });
-        yield();
-        ping();
     }
-
-//})();
\ No newline at end of file
+}
+
+ping();
+while (client.socket.is_connected) {
+    Object.keys(callbacks).forEach(function (e) {
+        try {
+            callbacks[e].cycle();
+        } catch (err) {
+            log(LOG_ERR, 'Callback ' + e + ' failed: ' + err);
+            delete callbacks[e];
+        }
+    });
+    yield();
+    ping();
+}
\ No newline at end of file
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index c54fc512e4..c8ae94187e 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,62 +1,58 @@
-(function () {
-
-	const settings = load('modopts.js', 'web');
-
-	load(settings.web_directory + '/lib/init.js');
-	load(settings.web_lib + 'auth.js');
-	require('filebase.js', 'FileBase');
-
-	var CHUNK_SIZE = 1024;
-
-	var reply = {};
-	if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-		typeof http_request.query.call !== 'undefined' && user.number > 0
-	) {
-
-		switch (http_request.query.call[0].toLowerCase()) {
-			case 'download-file':
-				if (typeof http_request.query.dir !== 'undefined' &&
-					typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
-					file_area.dir[http_request.query.dir[0]].can_download &&
-					typeof http_request.query.file !== 'undefined'
-				) {
-					var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
-					var file = null;
-					fileBase.some(function (e) {
-						if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
-							return false;
-						} else if (typeof e.path !== 'undefined') {
-							file = e;
-							return true;
-						}
-					});
-					if (file === null) break;
-					http_reply.header['Content-Type'] = 'application/octet-stream';
-					http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
-					http_reply.header['Content-Encoding'] = 'binary';
-					http_reply.header['Content-Length'] = file_size(file.path);
-					var f = new File(file.path);
-					f.open('rb');
-					for (var n = 0; n < f.length; n += CHUNK_SIZE) {
-						var r = f.length - f.position;
-						write(f.read(r > CHUNK_SIZE ? CHUNK_SIZE : r));
-						yield(false);
+const settings = load('modopts.js', 'web');
+
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
+require('filebase.js', 'FileBase');
+
+var CHUNK_SIZE = 1024;
+
+var reply = {};
+if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+	typeof http_request.query.call !== 'undefined' && user.number > 0
+) {
+
+	switch (http_request.query.call[0].toLowerCase()) {
+		case 'download-file':
+			if (typeof http_request.query.dir !== 'undefined' &&
+				typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
+				file_area.dir[http_request.query.dir[0]].can_download &&
+				typeof http_request.query.file !== 'undefined'
+			) {
+				var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
+				var file = null;
+				fileBase.some(function (e) {
+					if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
+						return false;
+					} else if (typeof e.path !== 'undefined') {
+						file = e;
+						return true;
 					}
-					f.close();
-					reply = false;
+				});
+				if (file === null) break;
+				http_reply.header['Content-Type'] = 'application/octet-stream';
+				http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
+				http_reply.header['Content-Encoding'] = 'binary';
+				http_reply.header['Content-Length'] = file_size(file.path);
+				var f = new File(file.path);
+				f.open('rb');
+				for (var n = 0; n < f.length; n += CHUNK_SIZE) {
+					var r = f.length - f.position;
+					write(f.read(r > CHUNK_SIZE ? CHUNK_SIZE : r));
+					yield(false);
 				}
-				break;
-			default:
-				break;
-		}
-
+				f.close();
+				reply = false;
+			}
+			break;
+		default:
+			break;
 	}
 
-	if (!reply) exit();
+}
 
-	reply = JSON.stringify(reply);
-	http_reply.header['Content-Type'] = 'application/json';
-	http_reply.header['Content-Length'] = reply.length;
-	write(reply);
+if (!reply) exit();
 
-})();
\ No newline at end of file
+reply = JSON.stringify(reply);
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
+write(reply);
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 7b4f5f6069..98fdd706c5 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -1,258 +1,256 @@
-(function () {
-    /*  This script is an interface between HTTP clients and the functions defined
-        in web/lib/forum.js.  A basic check for an authenticated, non-guest user
-        is done here; otherwise all permission checking is done at the function
-        level. */
+/*  This script is an interface between HTTP clients and the functions defined
+    in web/lib/forum.js.  A basic check for an authenticated, non-guest user
+    is done here; otherwise all permission checking is done at the function
+    level. */
+
+const settings = load('modopts.js', 'web');
+
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'forum.js');
+
+var reply = {};
+
+// There must be an API call, and the user must not be a guest or unknown
+if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+    typeof http_request.query.call !== 'undefined'
+) {
+
+    var handled = false;
+
+    // Authenticated calls
+    if (user.number > 0 && user.alias !== settings.guest) {
+
+        handled = true;
+
+        switch(http_request.query.call[0].toLowerCase()) {
+
+            case 'get-mail-unread-count':
+                reply.count = user.stats.mail_waiting;
+                break;
+
+            case 'get-mail-body':
+                if (typeof http_request.query.number !== 'undefined') {
+                    reply = getMailBody(http_request.query.number[0]);
+                }
+                break;
+
+            case 'get-signature':
+                reply.signature = getSignature();
+                break;
+
+            case 'post-reply':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.body !== 'undefined' &&
+                    typeof http_request.query.pid !== 'undefined'
+                ) {
+                    reply.success = postReply(
+                        http_request.query.sub[0],
+                        http_request.query.body[0],
+                        Number(http_request.query.pid[0])
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'post':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.to !== 'undefined' &&
+                    typeof http_request.query.subject !== 'undefined' &&
+                    typeof http_request.query.body !== 'undefined'
+                ) {
+                    reply.success = postNew(
+                        http_request.query.sub[0],
+                        http_request.query.to[0],
+                        http_request.query.subject[0],
+                        http_request.query.body[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'delete-message':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.number !== 'undefined'
+                ) {
+                    reply.success = deleteMessage(
+                        http_request.query.sub[0],
+                        http_request.query.number[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'delete-mail':
+                if (typeof http_request.query.number !== 'undefined') {
+                    reply.success = deleteMail(http_request.query.number);
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'set-scan-cfg':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.cfg !== 'undefined'
+                ) {
+                    reply.success = setScanCfg(
+                        http_request.query.sub[0],
+                        http_request.query.cfg[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'vote':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined' &&
+                    typeof http_request.query.up !== 'undefined' &&
+                    !(user.security.restrictions&UFLAG_V)
+                ) {
+                    reply.success = voteMessage(
+                        http_request.query.sub[0],
+                        http_request.query.id[0],
+                        http_request.query.up[0]
+                    );
+                } else {
+                    reply.success = false;
+                }
+                break;
+
+            case 'submit-poll-answers':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined' &&
+                    typeof http_request.query.answer !== 'undefined'
+                ) {
+                    reply.success = submitPollAnswers(
+                        http_request.query.sub[0],
+                        http_request.query.id[0],
+                        http_request.query.answer
+                    );
+                }
+                break;
+
+            case 'submit-poll':
+                if (typeof http_request.query.subject !== 'undefined' &&
+                    typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.votes !== 'undefined' &&
+                    typeof http_request.query.results !== 'undefined' &&
+                    typeof http_request.query.answer !== 'undefined'
+                ) {
+                    reply.success = postPoll(
+                        http_request.query.sub[0],
+                        http_request.query.subject[0],
+                        http_request.query.votes[0],
+                        http_request.query.results[0],
+                        http_request.query.answer,
+                        http_request.query.comment || []
+                    );
+                }
+                break;
+
+            default:
+                handled = false;
+                break;
 
-    const settings = load('modopts.js', 'web');
-
-    load(settings.web_directory + '/lib/init.js');
-    load(settings.web_lib + 'auth.js');
-    load(settings.web_lib + 'forum.js');
-
-    var reply = {};
-
-    // There must be an API call, and the user must not be a guest or unknown
-    if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-        typeof http_request.query.call !== 'undefined'
-    ) {
-
-        var handled = false;
-
-        // Authenticated calls
-        if (user.number > 0 && user.alias !== settings.guest) {
-
-            handled = true;
-
-            switch(http_request.query.call[0].toLowerCase()) {
-
-                case 'get-mail-unread-count':
-                    reply.count = user.stats.mail_waiting;
-                    break;
+        }
 
-                case 'get-mail-body':
-                    if (typeof http_request.query.number !== 'undefined') {
-                        reply = getMailBody(http_request.query.number[0]);
-                    }
-                    break;
-
-                case 'get-signature':
-                    reply.signature = getSignature();
-                    break;
-
-                case 'post-reply':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.body !== 'undefined' &&
-                        typeof http_request.query.pid !== 'undefined'
-                    ) {
-                        reply.success = postReply(
-                            http_request.query.sub[0],
-                            http_request.query.body[0],
-                            Number(http_request.query.pid[0])
-                        );
-                    } else {
-                        reply.success = false;
-                    }
-                    break;
-
-                case 'post':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.to !== 'undefined' &&
-                        typeof http_request.query.subject !== 'undefined' &&
-                        typeof http_request.query.body !== 'undefined'
-                    ) {
-                        reply.success = postNew(
-                            http_request.query.sub[0],
-                            http_request.query.to[0],
-                            http_request.query.subject[0],
-                            http_request.query.body[0]
-                        );
-                    } else {
-                        reply.success = false;
-                    }
-                    break;
+    }
 
-                case 'delete-message':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.number !== 'undefined'
-                    ) {
-                        reply.success = deleteMessage(
-                            http_request.query.sub[0],
-                            http_request.query.number[0]
-                        );
-                    } else {
-                        reply.success = false;
-                    }
-                    break;
+    // Unauthenticated calls
+    if (!handled) {
 
-                case 'delete-mail':
-                    if (typeof http_request.query.number !== 'undefined') {
-                        reply.success = deleteMail(http_request.query.number);
-                    } else {
-                        reply.success = false;
-                    }
-                    break;
+        switch(http_request.query.call[0].toLowerCase()) {
 
-                case 'set-scan-cfg':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.cfg !== 'undefined'
-                    ) {
-                        reply.success = setScanCfg(
-                            http_request.query.sub[0],
-                            http_request.query.cfg[0]
-                        );
-                    } else {
-                        reply.success = false;
-                    }
-                    break;
-
-                case 'vote':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.id !== 'undefined' &&
-                        typeof http_request.query.up !== 'undefined' &&
-                        !(user.security.restrictions&UFLAG_V)
-                    ) {
-                        reply.success = voteMessage(
-                            http_request.query.sub[0],
-                            http_request.query.id[0],
-                            http_request.query.up[0]
-                        );
-                    } else {
-                        reply.success = false;
-                    }
-                    break;
-
-                case 'submit-poll-answers':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.id !== 'undefined' &&
-                        typeof http_request.query.answer !== 'undefined'
-                    ) {
-                        reply.success = submitPollAnswers(
+            case 'get-thread-votes':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined'
+                ) {
+                    var id = parseInt(http_request.query.id[0]);
+                    if (!isNaN(id)) {
+                        reply = getVotesInThread(
                             http_request.query.sub[0],
-                            http_request.query.id[0],
-                            http_request.query.answer
+                            id
                         );
                     }
-                    break;
-
-                case 'submit-poll':
-                    if (typeof http_request.query.subject !== 'undefined' &&
-                        typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.votes !== 'undefined' &&
-                        typeof http_request.query.results !== 'undefined' &&
-                        typeof http_request.query.answer !== 'undefined'
-                    ) {
-                        reply.success = postPoll(
-                            http_request.query.sub[0],
-                            http_request.query.subject[0],
-                            http_request.query.votes[0],
-                            http_request.query.results[0],
-                            http_request.query.answer,
-                            http_request.query.comment || []
-                        );
+                }
+                break;
+
+            case 'get-sub-votes':
+                if (typeof http_request.query.sub !== 'undefined') {
+                    reply = getVotesInThreads(http_request.query.sub[0]);
+                }
+                break;
+
+            case 'get-poll-results':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.id !== 'undefined'
+                ) {
+                    reply = getUserPollData(
+                        http_request.query.sub[0],
+                        http_request.query.id[0]
+                    );
+                }
+                break;
+
+            case 'list-groups':
+                reply = listGroups();
+                break;
+
+            case 'list-subs':
+                if (typeof http_request.query.group !== 'undefined') {
+                    reply = listSubs(http_request.query.group[0]);
+                }
+                break;
+
+            case 'list-threads':
+                if (typeof http_request.query.sub !== 'undefined' &&
+                    typeof http_request.query.offset !== 'undefined'
+                ) {
+                    if (typeof http_request.query.count !== 'undefined') {
+                        var count = http_request.query.count[0];
                     }
-                    break;
-
-                default:
-                    handled = false;
-                    break;
-
-            }
-
-        }
-
-        // Unauthenticated calls
-        if (!handled) {
-
-            switch(http_request.query.call[0].toLowerCase()) {
-
-                case 'get-thread-votes':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.id !== 'undefined'
-                    ) {
-                        var id = parseInt(http_request.query.id[0]);
-                        if (!isNaN(id)) {
-                            reply = getVotesInThread(
-                                http_request.query.sub[0],
-                                id
-                            );
+                    reply = listThreads(
+                        http_request.query.sub[0],
+                        http_request.query.offset[0],
+                        count || settings.page_size
+                    ).threads;
+                }
+                break;
+
+            case 'get-group-unread-count':
+                if (typeof http_request.query.group !== 'undefined') {
+                    http_request.query.group.forEach(
+                        function(group) {
+                            reply[group] = getGroupUnreadCount(group);
                         }
-                    }
-                    break;
-
-                case 'get-sub-votes':
-                    if (typeof http_request.query.sub !== 'undefined') {
-                        reply = getVotesInThreads(http_request.query.sub[0]);
-                    }
-                    break;
-
-                case 'get-poll-results':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.id !== 'undefined'
-                    ) {
-                        reply = getUserPollData(
-                            http_request.query.sub[0],
-                            http_request.query.id[0]
-                        );
-                    }
-                    break;
-
-                case 'list-groups':
-                    reply = listGroups();
-                    break;
-
-                case 'list-subs':
-                    if (typeof http_request.query.group !== 'undefined') {
-                        reply = listSubs(http_request.query.group[0]);
-                    }
-                    break;
-
-                case 'list-threads':
-                    if (typeof http_request.query.sub !== 'undefined' &&
-                        typeof http_request.query.offset !== 'undefined'
-                    ) {
-                        if (typeof http_request.query.count !== 'undefined') {
-                            var count = http_request.query.count[0];
+                    );
+                }
+                break;
+
+            case 'get-sub-unread-count':
+                if (typeof http_request.query.sub !== 'undefined') {
+                    http_request.query.sub.forEach(
+                        function(sub) {
+                            reply[sub] = getSubUnreadCount(sub);
                         }
-                        reply = listThreads(
-                            http_request.query.sub[0],
-                            http_request.query.offset[0],
-                            count || settings.page_size
-                        ).threads;
-                    }
-                    break;
-
-                case 'get-group-unread-count':
-                    if (typeof http_request.query.group !== 'undefined') {
-                        http_request.query.group.forEach(
-                            function(group) {
-                                reply[group] = getGroupUnreadCount(group);
-                            }
-                        );
-                    }
-                    break;
-
-                case 'get-sub-unread-count':
-                    if (typeof http_request.query.sub !== 'undefined') {
-                        http_request.query.sub.forEach(
-                            function(sub) {
-                                reply[sub] = getSubUnreadCount(sub);
-                            }
-                        );
-                    }
-                    break;
-
-                default:
-                    break;
+                    );
+                }
+                break;
 
-            }
+            default:
+                break;
 
         }
 
     }
 
-    reply = JSON.stringify(reply);
-    http_reply.header['Content-Type'] = 'application/json';
-    http_reply.header['Content-Length'] = reply.length;
-    write(reply);
-})();
\ No newline at end of file
+}
+
+reply = JSON.stringify(reply);
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
+write(reply);
diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 1e414a6bb4..8183b30ec3 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -1,85 +1,83 @@
-(function () {
-	/*	Posts a notification message to a list of sub-boards when commits are made
-		to a GitHub repository.
+/*	Posts a notification message to a list of sub-boards when commits are made
+	to a GitHub repository.
 
-		- In your repository, go to Settings -> Webhooks -> Add webhook
-		- Payload URL: https://your-bbs-hostname/api/github.ssjs
-		- Content-type: application/json
-		- Secret: some-secret-passphrase-here
-		- Which events: Just the push event
-		- Active: checked
-		- Add webhook
+	- In your repository, go to Settings -> Webhooks -> Add webhook
+	- Payload URL: https://your-bbs-hostname/api/github.ssjs
+	- Content-type: application/json
+	- Secret: some-secret-passphrase-here
+	- Which events: Just the push event
+	- Active: checked
+	- Add webhook
 
-		- On your BBS server, edit ctrl/modopts.ini
-		- Add a [github-notify] section
-		- For each repository you wish to configure, add a key-value pair like:
-		- my-repository-name=secret,SUB
-		- Where 'secret' is the secret passphrase as configured above
-		- Where 'SUB' is the internal code of a sub-board to post to
-		- Multiple subs can be specified, separated by commas
-	*/
+	- On your BBS server, edit ctrl/modopts.ini
+	- Add a [github-notify] section
+	- For each repository you wish to configure, add a key-value pair like:
+	- my-repository-name=secret,SUB
+	- Where 'secret' is the secret passphrase as configured above
+	- Where 'SUB' is the internal code of a sub-board to post to
+	- Multiple subs can be specified, separated by commas
+*/
 
-	load('sbbsdefs.js');
-	load('hmac.js');
-	const options = load({}, 'modopts.js', 'github_notify');
-	load(system.exec_dir + '../web/lib/init.js');
+load('sbbsdefs.js');
+load('hmac.js');
+const options = load({}, 'modopts.js', 'github_notify');
+load(system.exec_dir + '../web/lib/init.js');
 
-	function b2h(str) {
-		return str.split('').map(function (e) {
-			var n = ascii(e).toString(16);
-			return n.length < 2 ? ('0' + n) : n;
-		}).join('');
-	}
+function b2h(str) {
+	return str.split('').map(function (e) {
+		var n = ascii(e).toString(16);
+		return n.length < 2 ? ('0' + n) : n;
+	}).join('');
+}
+
+function verify_signature(key, payload, hash) {
+	return (b2h(hmac_sha1(key, payload)) === hash);
+}
 
-	function verify_signature(key, payload, hash) {
-		return (b2h(hmac_sha1(key, payload)) === hash);
+try {
+	const hash = http_request.header['x-hub-signature'].split('=')[1];
+	const payload = JSON.parse(http_request.post_data);
+	if (typeof options[payload.repository.name] === 'undefined') {
+		throw 'Unknown repository ' + payload.repository.name;
+	}
+	const subs = options[payload.repository.name].split(',');
+	const secret = subs.shift();
+	if (!verify_signature(secret, http_request.post_data, hash)) {
+		throw 'GitHub signature mismatch';
 	}
+} catch (err) {
+	log(LOG_ERR, err);
+	exit();
+}
+
+const header = {
+	from: payload.head_commit.author.username,
+	to: 'All',
+	subject: 'Changes to ' + payload.repository.full_name
+};
+
+const body = payload.commits.map(function (e) {
+	const ret = [ 'Commit ID: ' + e.id, 'Author: ' + e.author.username ];
+	if (e.added.length) ret.push('Added: ' + e.added.join(', '));
+	if (e.removed.length) ret.push('Removed: ' + e.removed.join(', '));
+	if (e.modified.length) ret.push('Modified: ' + e.modified.join(', '));
+	ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
+	return ret.join('\r\n');
+}).concat('Repository URL: ' + payload.repository.url).join('\r\n\r\n');
 
+subs.forEach(function (sub) {
 	try {
-		const hash = http_request.header['x-hub-signature'].split('=')[1];
-		const payload = JSON.parse(http_request.post_data);
-		if (typeof options[payload.repository.name] === 'undefined') {
-			throw 'Unknown repository ' + payload.repository.name;
-		}
-		const subs = options[payload.repository.name].split(',');
-		const secret = subs.shift();
-		if (!verify_signature(secret, http_request.post_data, hash)) {
-			throw 'GitHub signature mismatch';
-		}
+		const mb = new MsgBase(sub);
+		if (!mb.open()) throw 'Failed to open message base ' + sub;
+		if (!mb.save_msg(header, body)) throw 'Failed to save message';
+		mb.close();
 	} catch (err) {
 		log(LOG_ERR, err);
-		exit();
 	}
+});
 
-	const header = {
-		from: payload.head_commit.author.username,
-		to: 'All',
-		subject: 'Changes to ' + payload.repository.full_name
-	};
-
-	const body = payload.commits.map(function (e) {
-		const ret = [ 'Commit ID: ' + e.id, 'Author: ' + e.author.username ];
-		if (e.added.length) ret.push('Added: ' + e.added.join(', '));
-		if (e.removed.length) ret.push('Removed: ' + e.removed.join(', '));
-		if (e.modified.length) ret.push('Modified: ' + e.modified.join(', '));
-		ret.push('', 'Message:', e.message, '', 'Commit URL:', e.url, '');
-		return ret.join('\r\n');
-	}).concat('Repository URL: ' + payload.repository.url).join('\r\n\r\n');
-
-	subs.forEach(function (sub) {
-		try {
-			const mb = new MsgBase(sub);
-			if (!mb.open()) throw 'Failed to open message base ' + sub;
-			if (!mb.save_msg(header, body)) throw 'Failed to save message';
-			mb.close();
-		} catch (err) {
-			log(LOG_ERR, err);
-		}
-	});
-
-	const _tf = new File(system.data_dir + '/github_notify');
-	if (_tf.open('w')) {
-		_tf.write(body);
-		_tf.close();
-	}
-})();
\ No newline at end of file
+const _tf = new File(system.data_dir + '/github_notify');
+if (_tf.open('w')) {
+	_tf.write(body);
+	_tf.close();
+}
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 74ce4388a8..58178b73e8 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,221 +1,219 @@
-(function () {
-	load('sbbsdefs.js');
-	load(system.exec_dir + '../web/lib/init.js');
-	load(settings.web_lib + '/auth.js');
-
-	if (user.alias !== settings.guest) exit();
-	if (!settings.user_registration) exit();
-
-	var MIN_ALIAS = 1,
-		MIN_REALNAME = 3,
-		MIN_NETMAIL = 6,
-		MIN_LOCATION = 4,
-		MIN_ADDRESS = 6,
-		MIN_PHONE = 3;
-
-	var reply = {
-		errors : [],
-		userNumber : 0
-	};
-
-	var prepUser = {
-		alias : '',
-		handle : '',
-		name : '',
-		netmail : '',
-		address : '',
-		zipcode : '',
-		location : '',
-		phone : '',
-		birthdate : '',
-		gender : '',
-		password : ''
-	};
-
-	function required(mask) {
-		return (system.new_user_questions&mask);
-	}
-
-	function cleanParam(param) {
-		if (paramExists(param)) {
-			return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
-		}
-		return "";
-	}
-
-	function paramExists(param) {
-		if (typeof http_request.query[param] !== 'undefined' &&
-			http_request.query[param][0] !== ''
-		) {
-			return true;
-		}
-		return false;
-	}
-
-	function paramLength(param) {
-		if (typeof http_request.query[param] === 'undefined') {
-			return 0;
-		} else if (http_request.query[param][0].replace(' ', '').length < 1) {
-			return 0;
-		} else if (cleanParam(param).length < 1) {
-			return 0;
-		} else {
-			return http_request.query[param][0].length;
-		}
-	}
-
-	function newUser() {
-		var usr = system.new_user(prepUser.alias);
-		if (typeof usr === 'number') {
-			reply.errors.push(locale.strings.api_register.error_failed);
-			return;
-		}
-		log(LOG_INFO, format(locale.strings.api_register.log_success, usr.number));
-		usr.security.password = prepUser.password;
-		for (var property in prepUser) {
-			if (property === 'alias' || property === 'password') continue;
-			usr[property] = prepUser[property];
-		}
-		reply.userNumber = usr.number;
-	}
-
-	// See if the hidden form fields were filled
-	if ((	paramExists('send-me-free-stuff') &&
-			http_request.query['send-me-free-stuff'][0] !== ''
-		) ||
-		(	paramExists('subscribe-to-newsletter') &&
-			http_request.query['subscribe-to-newsletter'][0] !== ''
-		)
-	) {
-		log(LOG_WARNING, locale.strings.api_register.log_bot_attempt);
-		exit();
-	}
-
-	if (system.newuser_password !== '' &&
-		(	typeof http_request.query['newuser-password'] === 'undefined' ||
-			http_request.query['newuser-password'][0] != system.newuser_password
-		)
-	) {
-		reply.errors.push(locale.strings.api_register.error_bad_syspass);
-	}
-
-	// More could be done to respect certain newuser question toggles
-	// (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
-
-	if (!paramExists('alias') ||
-		paramLength('alias') < MIN_ALIAS ||
-		paramLength('alias') > LEN_ALIAS
-	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_alias);
-	} else if (system.matchuser(http_request.query.alias[0]) > 0) {
-		reply.errors.push(locale.strings.api_register.error_alias_taken);
-	} else {
-		prepUser.alias = cleanParam('alias');
-		prepUser.handle = cleanParam('alias');
-	}
-
-	if ((!paramExists('password1') || !paramExists('password2')) ||
-		http_request.query.password1[0] !== http_request.query.password2[0]
-	) {
-		reply.errors.push(locale.strings.api_register.error_password_mismatch);
-	} else if (
-		paramLength('password1') < settings.minimum_password_length ||
-		paramLength('password1') > LEN_PASS
-	) {
-		reply.errors.push(
-			format(
-				locale.strings.api_register.error_password_length,
-				settings.minimum_password_length, LEN_PASS
-			)
-		);
-	} else {
-		prepUser.password = cleanParam('password1');
-	}
-
-	if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
-		reply.errors.push(locale.strings.api_register.error_email_required);
-	} else if (
-		(	paramLength('netmail') < MIN_NETMAIL ||
-			paramLength('netmail') > LEN_NETMAIL
-		) && !required(UQ_NONETMAIL)
-	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_email);
-	} else {
-		prepUser.netmail = cleanParam('netmail');
-	}
-
-	if (required(UQ_REALNAME) &&
-		(	!paramExists('realname') ||
-			paramLength('realname') < MIN_REALNAME ||
-			paramLength('realname') > LEN_NAME
-		)
+load('sbbsdefs.js');
+load(system.exec_dir + '../web/lib/init.js');
+load(settings.web_lib + '/auth.js');
+
+if (user.alias !== settings.guest) exit();
+if (!settings.user_registration) exit();
+
+var MIN_ALIAS = 1,
+	MIN_REALNAME = 3,
+	MIN_NETMAIL = 6,
+	MIN_LOCATION = 4,
+	MIN_ADDRESS = 6,
+	MIN_PHONE = 3;
+
+var reply = {
+	errors : [],
+	userNumber : 0
+};
+
+var prepUser = {
+	alias : '',
+	handle : '',
+	name : '',
+	netmail : '',
+	address : '',
+	zipcode : '',
+	location : '',
+	phone : '',
+	birthdate : '',
+	gender : '',
+	password : ''
+};
+
+function required(mask) {
+	return (system.new_user_questions&mask);
+}
+
+function cleanParam(param) {
+	if (paramExists(param)) {
+		return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
+	}
+	return "";
+}
+
+function paramExists(param) {
+	if (typeof http_request.query[param] !== 'undefined' &&
+		http_request.query[param][0] !== ''
 	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_name);
+		return true;
+	}
+	return false;
+}
+
+function paramLength(param) {
+	if (typeof http_request.query[param] === 'undefined') {
+		return 0;
+	} else if (http_request.query[param][0].replace(' ', '').length < 1) {
+		return 0;
+	} else if (cleanParam(param).length < 1) {
+		return 0;
 	} else {
-		prepUser.name = cleanParam('realname');
-	}
-
-	if (required(UQ_LOCATION) &&
-		(	!paramExists('location') ||
-			paramLength('location') < MIN_LOCATION ||
-			paramLength('location') > LEN_LOCATION
+		return http_request.query[param][0].length;
+	}
+}
+
+function newUser() {
+	var usr = system.new_user(prepUser.alias);
+	if (typeof usr === 'number') {
+		reply.errors.push(locale.strings.api_register.error_failed);
+		return;
+	}
+	log(LOG_INFO, format(locale.strings.api_register.log_success, usr.number));
+	usr.security.password = prepUser.password;
+	for (var property in prepUser) {
+		if (property === 'alias' || property === 'password') continue;
+		usr[property] = prepUser[property];
+	}
+	reply.userNumber = usr.number;
+}
+
+// See if the hidden form fields were filled
+if ((	paramExists('send-me-free-stuff') &&
+		http_request.query['send-me-free-stuff'][0] !== ''
+	) ||
+	(	paramExists('subscribe-to-newsletter') &&
+		http_request.query['subscribe-to-newsletter'][0] !== ''
+	)
+) {
+	log(LOG_WARNING, locale.strings.api_register.log_bot_attempt);
+	exit();
+}
+
+if (system.newuser_password !== '' &&
+	(	typeof http_request.query['newuser-password'] === 'undefined' ||
+		http_request.query['newuser-password'][0] != system.newuser_password
+	)
+) {
+	reply.errors.push(locale.strings.api_register.error_bad_syspass);
+}
+
+// More could be done to respect certain newuser question toggles
+// (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
+
+if (!paramExists('alias') ||
+	paramLength('alias') < MIN_ALIAS ||
+	paramLength('alias') > LEN_ALIAS
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_alias);
+} else if (system.matchuser(http_request.query.alias[0]) > 0) {
+	reply.errors.push(locale.strings.api_register.error_alias_taken);
+} else {
+	prepUser.alias = cleanParam('alias');
+	prepUser.handle = cleanParam('alias');
+}
+
+if ((!paramExists('password1') || !paramExists('password2')) ||
+	http_request.query.password1[0] !== http_request.query.password2[0]
+) {
+	reply.errors.push(locale.strings.api_register.error_password_mismatch);
+} else if (
+	paramLength('password1') < settings.minimum_password_length ||
+	paramLength('password1') > LEN_PASS
+) {
+	reply.errors.push(
+		format(
+			locale.strings.api_register.error_password_length,
+			settings.minimum_password_length, LEN_PASS
 		)
-	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_location);
-	} else {
-		prepUser.location = cleanParam('location');
-	}
-
-	if (required(UQ_ADDRESS) &&
-		(	!paramExists('address') ||
-			paramLength('address') < MIN_ADDRESS ||
-			paramLength('address') > LEN_ADDRESS ||
-			!paramExists('zipcode') ||
-			paramLength('zipcode') < 3 ||
-			paramLength('zipcode') > LEN_ADDRESS
-		)
-	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_street_address);
-	} else {
-		prepUser.address = cleanParam('address');
-		prepUser.zipcode = cleanParam('zipcode');
-	}
-
-	if (required(UQ_PHONE) &&
-		(	!paramExists('phone') ||
-			paramLength('phone') < MIN_PHONE ||
-			paramLength('phone') > LEN_PHONE
-		)
-	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_phone);
-	} else {
-		prepUser.phone = cleanParam('phone');
-	}
-
-	if (required(UQ_SEX) &&
-		(	!paramExists('gender') ||
-			paramLength('gender') != 1 ||
-			['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
-		)
-	) {
-		reply.errors.push(locale.strings.api_register.error_invalid_gender);
-	} else {
-		prepUser.gender = http_request.query.gender[0];
-	}
-
-	if (paramExists('birth') &&
-		http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
-	) {
-		// Should really test for valid date (and date format per system config)
-		prepUser.birthdate = cleanParam('birth');
-	} else if (required(UQ_BIRTH)) {
-		reply.errors.push(locale.strings.api_register.error_invalid_birthdate);
-	}
-
-	if (reply.errors.length < 1) newUser();
-
-	reply = JSON.stringify(reply);
-	http_reply.header['Content-Type'] = 'application/json';
-	http_reply.header['Content-Length'] = reply.length;
-	write(reply);
-})();
\ No newline at end of file
+	);
+} else {
+	prepUser.password = cleanParam('password1');
+}
+
+if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
+	reply.errors.push(locale.strings.api_register.error_email_required);
+} else if (
+	(	paramLength('netmail') < MIN_NETMAIL ||
+		paramLength('netmail') > LEN_NETMAIL
+	) && !required(UQ_NONETMAIL)
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_email);
+} else {
+	prepUser.netmail = cleanParam('netmail');
+}
+
+if (required(UQ_REALNAME) &&
+	(	!paramExists('realname') ||
+		paramLength('realname') < MIN_REALNAME ||
+		paramLength('realname') > LEN_NAME
+	)
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_name);
+} else {
+	prepUser.name = cleanParam('realname');
+}
+
+if (required(UQ_LOCATION) &&
+	(	!paramExists('location') ||
+		paramLength('location') < MIN_LOCATION ||
+		paramLength('location') > LEN_LOCATION
+	)
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_location);
+} else {
+	prepUser.location = cleanParam('location');
+}
+
+if (required(UQ_ADDRESS) &&
+	(	!paramExists('address') ||
+		paramLength('address') < MIN_ADDRESS ||
+		paramLength('address') > LEN_ADDRESS ||
+		!paramExists('zipcode') ||
+		paramLength('zipcode') < 3 ||
+		paramLength('zipcode') > LEN_ADDRESS
+	)
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_street_address);
+} else {
+	prepUser.address = cleanParam('address');
+	prepUser.zipcode = cleanParam('zipcode');
+}
+
+if (required(UQ_PHONE) &&
+	(	!paramExists('phone') ||
+		paramLength('phone') < MIN_PHONE ||
+		paramLength('phone') > LEN_PHONE
+	)
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_phone);
+} else {
+	prepUser.phone = cleanParam('phone');
+}
+
+if (required(UQ_SEX) &&
+	(	!paramExists('gender') ||
+		paramLength('gender') != 1 ||
+		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
+	)
+) {
+	reply.errors.push(locale.strings.api_register.error_invalid_gender);
+} else {
+	prepUser.gender = http_request.query.gender[0];
+}
+
+if (paramExists('birth') &&
+	http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
+) {
+	// Should really test for valid date (and date format per system config)
+	prepUser.birthdate = cleanParam('birth');
+} else if (required(UQ_BIRTH)) {
+	reply.errors.push(locale.strings.api_register.error_invalid_birthdate);
+}
+
+if (reply.errors.length < 1) newUser();
+
+reply = JSON.stringify(reply);
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
+write(reply);
diff --git a/web/root/api/sbbsimsg.ssjs b/web/root/api/sbbsimsg.ssjs
index 510b17e91d..1b6a5c3d48 100644
--- a/web/root/api/sbbsimsg.ssjs
+++ b/web/root/api/sbbsimsg.ssjs
@@ -1,29 +1,28 @@
-(function () {
-    load("sbbsdefs.js");
-    load("nodedefs.js");
-    const settings = load('modopts.js', 'web');
-    load(settings.web_directory + '/lib/init.js');
-    load(settings.web_lib + 'auth.js');
-    const sbbsimsg = load({}, "sbbsimsg_lib.js");
 
-    var reply = {};
+load("sbbsdefs.js");
+load("nodedefs.js");
+const settings = load('modopts.js', 'web');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
+const sbbsimsg = load({}, "sbbsimsg_lib.js");
 
-    if (user.number > 0 && user.alias != settings.guest
-        && typeof http_request.query.call != 'undefined'
-        && http_request.query.call[0] == 'send_telegram'
-        && typeof http_request.query.host != 'undefined'
-        && typeof http_request.query.username != 'undefined'
-        && typeof http_request.query.message != 'undefined'
-    ) {
-        sbbsimsg.send_msg(
-            http_request.query.username[0] + '@' + http_request.query.host[0],
-            http_request.query.message[0],
-            user.alias
-        );
-    }
+var reply = {};
 
-    reply = JSON.stringify(reply);
-    http_reply.header['Content-Type'] = 'application/json';
-    http_reply.header['Content-Length'] = reply.length;
-    write(reply);
-})();
\ No newline at end of file
+if (user.number > 0 && user.alias != settings.guest
+    && typeof http_request.query.call != 'undefined'
+    && http_request.query.call[0] == 'send_telegram'
+    && typeof http_request.query.host != 'undefined'
+    && typeof http_request.query.username != 'undefined'
+    && typeof http_request.query.message != 'undefined'
+) {
+    sbbsimsg.send_msg(
+        http_request.query.username[0] + '@' + http_request.query.host[0],
+        http_request.query.message[0],
+        user.alias
+    );
+}
+
+reply = JSON.stringify(reply);
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
+write(reply);
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index be1103303e..060f1643d9 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -1,99 +1,97 @@
-(function () {
-	load('sbbsdefs.js');
-	load('nodedefs.js');
-	const settings = load('modopts.js', 'web');
+load('sbbsdefs.js');
+load('nodedefs.js');
+const settings = load('modopts.js', 'web');
 
-	load(settings.web_directory + '/lib/init.js');
-	load(settings.web_lib + 'auth.js');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
 
-	var reply = {};
+var reply = {};
 
-	if ((http_request.method === 'GET' || http_request.method === 'POST') &&
-		typeof http_request.query.call !== 'undefined' &&
-		user.number > 0
-	) {
+if ((http_request.method === 'GET' || http_request.method === 'POST') &&
+	typeof http_request.query.call !== 'undefined' &&
+	user.number > 0
+) {
 
-		switch (http_request.query.call[0]) {
+	switch (http_request.query.call[0]) {
 
-			case 'node-list':
-				var usr = new User(1);
-				reply = system.node_list.reduce(function (a, c, i) {
-					if (c.status !== 3) return a;
-					usr.number = c.useron;
-					a.push({
-						node: i,
-						status: format(NodeStatus[c.status], c.aux, c.extaux),
-						action: format(NodeAction[c.action], c.aux, c.extaux),
-						user: usr.alias,
-						connection: usr.connection
-					});
-					return a;
-				}, []);
-				for (var un = 1; un < system.lastuser; un++) {
-					usr.number = un;
-					if (usr.connection !== 'HTTP') continue;
-					if (usr.alias === settings.guest) continue;
-					if (usr.settings&USER_QUIET) continue;
-					if (usr.logontime < time() - settings.inactivity) continue;
-					var webAction = auth_lib.getSessionValue(usr.number, 'action');
-					if (webAction === null) continue;
-					reply.push({
-						status: '',
-						action: locale.strings.api_system.nodelist_action_prefix + ' ' + webAction,
-						user: usr.alias,
-						connection: 'W'
-					});
-				}
-				usr = undefined;
-				break;
+		case 'node-list':
+			var usr = new User(1);
+			reply = system.node_list.reduce(function (a, c, i) {
+				if (c.status !== 3) return a;
+				usr.number = c.useron;
+				a.push({
+					node: i,
+					status: format(NodeStatus[c.status], c.aux, c.extaux),
+					action: format(NodeAction[c.action], c.aux, c.extaux),
+					user: usr.alias,
+					connection: usr.connection
+				});
+				return a;
+			}, []);
+			for (var un = 1; un < system.lastuser; un++) {
+				usr.number = un;
+				if (usr.connection !== 'HTTP') continue;
+				if (usr.alias === settings.guest) continue;
+				if (usr.settings&USER_QUIET) continue;
+				if (usr.logontime < time() - settings.inactivity) continue;
+				var webAction = auth_lib.getSessionValue(usr.number, 'action');
+				if (webAction === null) continue;
+				reply.push({
+					status: '',
+					action: locale.strings.api_system.nodelist_action_prefix + ' ' + webAction,
+					user: usr.alias,
+					connection: 'W'
+				});
+			}
+			usr = undefined;
+			break;
 
-			case 'send-telegram':
-				if (user.alias === settings.guest) break;
-				if (typeof http_request.query.user === 'undefined') break;
-				if (typeof http_request.query.telegram === 'undefined' ||
-					http_request.query.telegram[0] === ''
-				) {
-					break;
-				}
-				if (http_request.query.telegram[0].length >
-					settings.maximum_telegram_length
-				) {
-					break;
-				}
-				var un = system.matchuser(http_request.query.user[0]);
-				if (un < 1) break;
-				system.put_telegram(
-					un, format(
-						locale.strings.api_system.telegram_header_format,
-						user.alias, (new Date()).toLocaleString()
-					) + '\r\n' + http_request.query.telegram[0] + '\r\n'
-				);
+		case 'send-telegram':
+			if (user.alias === settings.guest) break;
+			if (typeof http_request.query.user === 'undefined') break;
+			if (typeof http_request.query.telegram === 'undefined' ||
+				http_request.query.telegram[0] === ''
+			) {
 				break;
-
-			case 'get-telegram':
-				if (user.alias === settings.guest) break;
-				reply.telegram = system.get_telegram(user.number);
+			}
+			if (http_request.query.telegram[0].length >
+				settings.maximum_telegram_length
+			) {
 				break;
+			}
+			var un = system.matchuser(http_request.query.user[0]);
+			if (un < 1) break;
+			system.put_telegram(
+				un, format(
+					locale.strings.api_system.telegram_header_format,
+					user.alias, (new Date()).toLocaleString()
+				) + '\r\n' + http_request.query.telegram[0] + '\r\n'
+			);
+			break;
 
-			case 'set-xtrn-intent':
-				if (user.alias === settings.guest) break;
-				if (typeof http_request.query.code === 'undefined') break;
-				if (http_request.query.code[0].length > 8) break;
-				if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
-					break;
-				}
-				auth_lib.setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
-				break;
+		case 'get-telegram':
+			if (user.alias === settings.guest) break;
+			reply.telegram = system.get_telegram(user.number);
+			break;
 
-			default:
+		case 'set-xtrn-intent':
+			if (user.alias === settings.guest) break;
+			if (typeof http_request.query.code === 'undefined') break;
+			if (http_request.query.code[0].length > 8) break;
+			if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
 				break;
+			}
+			auth_lib.setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
+			break;
 
-		}
+		default:
+			break;
 
 	}
 
-	reply = JSON.stringify(reply);
-	http_reply.header['Content-Type'] = 'application/json';
-	http_reply.header['Content-Length'] = reply.length;
-	write(reply);
-})();
\ No newline at end of file
+}
+
+reply = JSON.stringify(reply);
+http_reply.header['Content-Type'] = 'application/json';
+http_reply.header['Content-Length'] = reply.length;
+write(reply);
-- 
GitLab


From 49b1d266b842c6e60e4fd7dc45563ac939b59cb4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 29 Jul 2019 23:58:02 -0400
Subject: [PATCH 502/752] Don't mess with the 'page' var; other things might
 want it later.

---
 web/lib/pages.js | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/web/lib/pages.js b/web/lib/pages.js
index e26519f7e0..55a7b0035b 100644
--- a/web/lib/pages.js
+++ b/web/lib/pages.js
@@ -136,14 +136,14 @@ function getPage(page) {
 
 	var ret = '';
 
-	page = settings.web_pages + page;
+	var p = settings.web_pages + page;
 
-	if (!file_exists(page)) return ret;
+	if (!file_exists(p)) return ret;
 
-	var ext = file_getext(page).toUpperCase();
+	var ext = file_getext(p).toUpperCase();
 
 	if (user.alias != settings.guest) {
-		var ctrl = getCtrlLine(page);
+		var ctrl = getCtrlLine(p);
 		if (typeof ctrl !== 'undefined' && !ctrl.options.hidden) {
 			setSessionValue(user.number, 'action', ctrl.title);
 		}
@@ -151,18 +151,18 @@ function getPage(page) {
 
 	switch(ext) {
 		case '.SSJS':
-			if (ext === '.SSJS' && page.search(/\.xjs\.ssjs$/i) >= 0) break;
+			if (ext === '.SSJS' && p.search(/\.xjs\.ssjs$/i) >= 0) break;
 			(function () {
-				load(page, true);
+				load(p, true);
 			})();
 			break;
 		case '.XJS':
 			(function () {
-				load(xjs_compile(page), true);
+				load(xjs_compile(p), true);
 			})();
 			break;
 		case '.HTML':
-			var f = new File(page);
+			var f = new File(p);
 			f.open('r');
 			if (f.is_open) {
 				ret = f.read();
@@ -170,7 +170,7 @@ function getPage(page) {
 			}
 			break;
 		case '.TXT':
-			var f = new File(page);
+			var f = new File(p);
 			f.open('r');
 			if (f.is_open) {
 				ret = '<pre>' + f.read() + '</pre>';
-- 
GitLab


From 907a6e2a6be4b949047524c0eb99b89644564f50 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 30 Jul 2019 13:06:02 -0400
Subject: [PATCH 503/752] Use require() where currently sensible. (libs to be
 updated (again) for require()ability). Where require() not
 possible/applicable, use var instead of const at the top level.

---
 web/lib/avatars.js                        |   2 +-
 web/lib/request.js                        |   2 +-
 web/pages/.examples/000-mail.xjs          |   4 ++--
 web/pages/.examples/000-register.xjs      |   2 +-
 web/pages/.examples/001-forum.ssjs        |   4 ++--
 web/root/api/attachments.ssjs             |   2 +-
 web/root/api/events.ssjs                  |   4 ++--
 web/root/api/files.ssjs                   |   2 +-
 web/root/api/forum.ssjs                   |   2 +-
 web/root/api/github.ssjs                  |  20 ++++++++++----------
 web/root/api/register.ssjs                |   2 +-
 web/root/api/sbbsimsg.ssjs                |   8 ++++----
 web/root/api/system.ssjs                  |   6 +++---
 web/root/images/favicon.ico               | Bin 3774 -> 318 bytes
 web/root/index.xjs                        |   8 ++------
 web/sidebar/.examples/003-systemStats.xjs |   2 +-
 16 files changed, 33 insertions(+), 37 deletions(-)

diff --git a/web/lib/avatars.js b/web/lib/avatars.js
index fc5256dec3..24c8da2078 100644
--- a/web/lib/avatars.js
+++ b/web/lib/avatars.js
@@ -1,4 +1,4 @@
-const avatar_lib = load({}, 'avatar_lib.js');
+var avatar_lib = load({}, 'avatar_lib.js');
 
 function Avatars() {
 
diff --git a/web/lib/request.js b/web/lib/request.js
index 945fae6a54..828cb82674 100644
--- a/web/lib/request.js
+++ b/web/lib/request.js
@@ -1,5 +1,5 @@
 // Helpers for http_request
-const Request = {
+var Request = {
     // Query parameter p exists and first instance is of optional type t
     has_param: function (p) {
       return (Array.isArray(http_request.query[p]) && http_request.query[p].length);
diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index 8611bfa64e..6f767e8347 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -4,8 +4,8 @@
     if (user.number == 0 || user.alias == settings.guest) exit();
     load('sbbsdefs.js');
     load(settings.web_lib + 'forum.js');
-    const _mail_tab = http_request.query.tab ? http_request.query.tab[0] : 'inbox';
-    const _mail = get_mail_headers(_mail_tab, true);
+    var _mail_tab = http_request.query.tab ? http_request.query.tab[0] : 'inbox';
+    var _mail = get_mail_headers(_mail_tab, true);
     function _active_tab(tab) {
         return tab == _mail_tab ? 'active' : '';
     }
diff --git a/web/pages/.examples/000-register.xjs b/web/pages/.examples/000-register.xjs
index d697df6baa..3d70bfaac7 100644
--- a/web/pages/.examples/000-register.xjs
+++ b/web/pages/.examples/000-register.xjs
@@ -10,7 +10,7 @@
 <?xjs
 	if (user.number > 0 && user.alias !== settings.guest) exit();
 
-	load('sbbsdefs.js');
+	requre('sbbsdefs.js', 'SYS_CLOSED');
 
 	function required(mask) {
 		return ((system.new_user_questions&mask) ? (' ' + locale.strings.page_register.stat_suffix_field_required) : '');
diff --git a/web/pages/.examples/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
index 45961183bc..06868d409d 100644
--- a/web/pages/.examples/001-forum.ssjs
+++ b/web/pages/.examples/001-forum.ssjs
@@ -1,6 +1,6 @@
 //Forum
 
-load('sbbsdefs.js');
+require('sbbsdefs.js', 'SYS_CLOSED');
 load(settings.web_lib + 'forum.js');
 load(settings.web_lib + 'avatars.js');
 
@@ -333,7 +333,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		// This is a normal message
 		} else {
-			writeln(formatMessage(body, false, settings.forum_extended_ascii && !header.is_utf8));
+			writeln(formatMessage(body, false, settings.forum_extended_ascii));
 			writeln(strings.message.body.close);
 		}
 
diff --git a/web/root/api/attachments.ssjs b/web/root/api/attachments.ssjs
index 77b4b6d523..50411bb234 100644
--- a/web/root/api/attachments.ssjs
+++ b/web/root/api/attachments.ssjs
@@ -1,6 +1,6 @@
 require('sbbsdefs.js', 'SYS_CLOSED');
 
-const settings = load('modopts.js', 'web');
+var settings = load('modopts.js', 'web');
 
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index d48bdd2e53..01135feede 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -1,5 +1,5 @@
-load('sbbsdefs.js');
-const settings = load('modopts.js', 'web');
+require('sbbsdefs.js', 'SYS_CLOSED');
+var settings = load('modopts.js', 'web');
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
 load(settings.web_lib + 'request.js');
diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index c8ae94187e..cfcc48d948 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,4 +1,4 @@
-const settings = load('modopts.js', 'web');
+var settings = load('modopts.js', 'web');
 
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
diff --git a/web/root/api/forum.ssjs b/web/root/api/forum.ssjs
index 98fdd706c5..8835437a2f 100644
--- a/web/root/api/forum.ssjs
+++ b/web/root/api/forum.ssjs
@@ -3,7 +3,7 @@
     is done here; otherwise all permission checking is done at the function
     level. */
 
-const settings = load('modopts.js', 'web');
+var settings = load('modopts.js', 'web');
 
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
diff --git a/web/root/api/github.ssjs b/web/root/api/github.ssjs
index 8183b30ec3..48a5d889b6 100644
--- a/web/root/api/github.ssjs
+++ b/web/root/api/github.ssjs
@@ -18,9 +18,9 @@
 	- Multiple subs can be specified, separated by commas
 */
 
-load('sbbsdefs.js');
-load('hmac.js');
-const options = load({}, 'modopts.js', 'github_notify');
+require('sbbsdefs.js', 'SYS_CLOSED');
+require('hmac.js', 'hmac_sha1');
+var options = load({}, 'modopts.js', 'github_notify');
 load(system.exec_dir + '../web/lib/init.js');
 
 function b2h(str) {
@@ -35,13 +35,13 @@ function verify_signature(key, payload, hash) {
 }
 
 try {
-	const hash = http_request.header['x-hub-signature'].split('=')[1];
-	const payload = JSON.parse(http_request.post_data);
+	var hash = http_request.header['x-hub-signature'].split('=')[1];
+	var payload = JSON.parse(http_request.post_data);
 	if (typeof options[payload.repository.name] === 'undefined') {
 		throw 'Unknown repository ' + payload.repository.name;
 	}
-	const subs = options[payload.repository.name].split(',');
-	const secret = subs.shift();
+	var subs = options[payload.repository.name].split(',');
+	var secret = subs.shift();
 	if (!verify_signature(secret, http_request.post_data, hash)) {
 		throw 'GitHub signature mismatch';
 	}
@@ -50,13 +50,13 @@ try {
 	exit();
 }
 
-const header = {
+var header = {
 	from: payload.head_commit.author.username,
 	to: 'All',
 	subject: 'Changes to ' + payload.repository.full_name
 };
 
-const body = payload.commits.map(function (e) {
+var body = payload.commits.map(function (e) {
 	const ret = [ 'Commit ID: ' + e.id, 'Author: ' + e.author.username ];
 	if (e.added.length) ret.push('Added: ' + e.added.join(', '));
 	if (e.removed.length) ret.push('Removed: ' + e.removed.join(', '));
@@ -76,7 +76,7 @@ subs.forEach(function (sub) {
 	}
 });
 
-const _tf = new File(system.data_dir + '/github_notify');
+var _tf = new File(system.data_dir + '/github_notify');
 if (_tf.open('w')) {
 	_tf.write(body);
 	_tf.close();
diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 58178b73e8..020a9c9216 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,4 +1,4 @@
-load('sbbsdefs.js');
+require('sbbsdefs.js', 'SYS_CLOSED');
 load(system.exec_dir + '../web/lib/init.js');
 load(settings.web_lib + '/auth.js');
 
diff --git a/web/root/api/sbbsimsg.ssjs b/web/root/api/sbbsimsg.ssjs
index 1b6a5c3d48..0c3eba0bd9 100644
--- a/web/root/api/sbbsimsg.ssjs
+++ b/web/root/api/sbbsimsg.ssjs
@@ -1,10 +1,10 @@
 
-load("sbbsdefs.js");
-load("nodedefs.js");
-const settings = load('modopts.js', 'web');
+require('sbbsdefs.js', 'SYS_CLOSED'); // Is this actually used?
+require('nodedefs.js', 'NODE_WFC'); // Is this actually used?
+var settings = load('modopts.js', 'web');
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
-const sbbsimsg = load({}, "sbbsimsg_lib.js");
+var sbbsimsg = load({}, "sbbsimsg_lib.js");
 
 var reply = {};
 
diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 060f1643d9..e7ff5bb27d 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -1,6 +1,6 @@
-load('sbbsdefs.js');
-load('nodedefs.js');
-const settings = load('modopts.js', 'web');
+require('sbbsdefs.js', 'SYS_CLOSED');
+require('nodedefs.js', 'NODE_WFC');
+var settings = load('modopts.js', 'web');
 
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
diff --git a/web/root/images/favicon.ico b/web/root/images/favicon.ico
index bcf9f091444f146b69a3da4e0eeb6f35fb84baf0..969e54f61adc18b88b9b3c583db1f2d12c2e6c69 100644
GIT binary patch
literal 318
zcmZQzU}Ruq5D*YxU}Run&|qX>5My9q&|qL-5MW?nP+$PbvoL@_1BifPkQ|iBz_9s0
z!~g&P8H^A8XJA<Jnt>r>3d8sB-x(M>4l(@y_lJRjVIBhmLjeN=!*K=%h7WLUAVz3t
zSQ-d~mzOCiwWXznySOPSDW#=_JBleODXFD}JDMqh1V98xKuIYr2|_bKX$26S29Z}w
TQvw;5mX>yU7I6^d21+mhnodPt

literal 3774
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
bME?NMpfHk$1m-_}2>l<*|34qf$3g)Bs9H-!

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 2705bb4f0e..0c9bd773d6 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -1,7 +1,5 @@
-<?xjs (function () { ?>
-
 <?xjs
-	const settings = load('modopts.js', 'web');
+	var settings = load('modopts.js', 'web');
 	load(settings.web_directory + '/lib/init.js');
 
 	load(settings.web_lib + 'auth.js');
@@ -216,6 +214,4 @@
 
 	</body>
 
-</html>
-
-<?xjs })(); ?>
\ No newline at end of file
+</html>
\ No newline at end of file
diff --git a/web/sidebar/.examples/003-systemStats.xjs b/web/sidebar/.examples/003-systemStats.xjs
index 1a3afe8775..e7f0b36796 100644
--- a/web/sidebar/.examples/003-systemStats.xjs
+++ b/web/sidebar/.examples/003-systemStats.xjs
@@ -1,5 +1,5 @@
 <?xjs
-  const _sb_niu = system.node_list.reduce(function (a, c) {
+  var _sb_niu = system.node_list.reduce(function (a, c) {
     return (c.status == 3 ? a + 1 : a);
   }, 0);
   require('file_size.js', 'file_size_str');
-- 
GitLab


From 5a10cf2dd0b7f7c380a1a28505b00952fa1c3614 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 30 Jul 2019 13:08:57 -0400
Subject: [PATCH 504/752] Whoops.

---
 web/root/images/favicon.ico | Bin 318 -> 3774 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/web/root/images/favicon.ico b/web/root/images/favicon.ico
index 969e54f61adc18b88b9b3c583db1f2d12c2e6c69..bcf9f091444f146b69a3da4e0eeb6f35fb84baf0 100644
GIT binary patch
literal 3774
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
bME?NMpfHk$1m-_}2>l<*|34qf$3g)Bs9H-!

literal 318
zcmZQzU}Ruq5D*YxU}Run&|qX>5My9q&|qL-5MW?nP+$PbvoL@_1BifPkQ|iBz_9s0
z!~g&P8H^A8XJA<Jnt>r>3d8sB-x(M>4l(@y_lJRjVIBhmLjeN=!*K=%h7WLUAVz3t
zSQ-d~mzOCiwWXznySOPSDW#=_JBleODXFD}JDMqh1V98xKuIYr2|_bKX$26S29Z}w
TQvw;5mX>yU7I6^d21+mhnodPt

-- 
GitLab


From 26110649974f853a858ebc2ea86ada190820e3db Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 30 Jul 2019 15:43:27 -0400
Subject: [PATCH 505/752] No skippo last user por favor.

---
 web/pages/.examples/More/001-userlist.xjs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/web/pages/.examples/More/001-userlist.xjs b/web/pages/.examples/More/001-userlist.xjs
index bf63f03c7a..ea9772a34a 100644
--- a/web/pages/.examples/More/001-userlist.xjs
+++ b/web/pages/.examples/More/001-userlist.xjs
@@ -140,8 +140,8 @@
 
 	function loadUsers(offset, pageSize) {
 		var users = [];
-    var usr = new User(1);
-		for (var u = 1; u < system.lastuser; u++) {
+	    var usr = new User(1);
+		for (var u = 1; u <= system.lastuser; u++) {
 			usr.number = u;
 			if (usr.settings&USER_DELETED ||
 				usr.compare_ars('REST Q') ||
@@ -152,7 +152,7 @@
 			}
 			users.push(copyProperties(usr, {}));
 		}
-    usr = undefined;
+    	usr = undefined;
 		users.sort(sortUsers);
 		return users.slice(offset, offset + pageSize);
 	}
-- 
GitLab


From 995febd326e1d983d098c7510db345da5805fb59 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 30 Jul 2019 23:37:08 -0400
Subject: [PATCH 506/752] preventDefault on game link click to stop stupid jump
 to anchor.

---
 web/pages/.examples/003-games.xjs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index e92ed5076f..6c82640e70 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -83,7 +83,10 @@
             var li = $('#xtrn-item-template').clone();
             var a = $(li).find('a')[0];
             $(a).text(xx.n);
-            $(a).click(function () { launchXtrn(xx.c); });
+            $(a).click(function (evt) {
+                evt.preventDefault();
+                launchXtrn(xx.c);
+            });
             $(ul).append(li);
             $(li).show();
         });
-- 
GitLab


From 8477dfb192ca91ee3ae3cf263ddebd029df2b3a5 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 31 Jul 2019 01:43:28 -0400
Subject: [PATCH 507/752] auth_lib isn't a thing here right now

---
 web/root/api/system.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index e7ff5bb27d..8efd10fec5 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -34,7 +34,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				if (usr.alias === settings.guest) continue;
 				if (usr.settings&USER_QUIET) continue;
 				if (usr.logontime < time() - settings.inactivity) continue;
-				var webAction = auth_lib.getSessionValue(usr.number, 'action');
+				var webAction = getSessionValue(usr.number, 'action');
 				if (webAction === null) continue;
 				reply.push({
 					status: '',
@@ -81,7 +81,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 			if (typeof xtrn_area.prog[http_request.query.code[0]] === 'undefined') {
 				break;
 			}
-			auth_lib.setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
+			setSessionValue(user.number, 'xtrn', http_request.query.code[0]);
 			break;
 
 		default:
-- 
GitLab


From 1bb7bfa2216f38e7ff77bb0558f039130ce79666 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 31 Jul 2019 08:51:22 -0400
Subject: [PATCH 508/752] Typo

---
 web/pages/.examples/000-register.xjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/pages/.examples/000-register.xjs b/web/pages/.examples/000-register.xjs
index 3d70bfaac7..e9f6b187fb 100644
--- a/web/pages/.examples/000-register.xjs
+++ b/web/pages/.examples/000-register.xjs
@@ -10,7 +10,7 @@
 <?xjs
 	if (user.number > 0 && user.alias !== settings.guest) exit();
 
-	requre('sbbsdefs.js', 'SYS_CLOSED');
+	require('sbbsdefs.js', 'SYS_CLOSED');
 
 	function required(mask) {
 		return ((system.new_user_questions&mask) ? (' ' + locale.strings.page_register.stat_suffix_field_required) : '');
-- 
GitLab


From d96456a97eb6fc483d32ee0c7d82eabda700543b Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 31 Jul 2019 14:49:07 -0400
Subject: [PATCH 509/752] Load things properly.

---
 web/root/api/register.ssjs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 020a9c9216..fb3641ea6f 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -1,6 +1,7 @@
 require('sbbsdefs.js', 'SYS_CLOSED');
-load(system.exec_dir + '../web/lib/init.js');
-load(settings.web_lib + '/auth.js');
+var settings = load('modopts.js', 'web');
+load(settings.web_directory + '/lib/init.js');
+load(settings.web_lib + 'auth.js');
 
 if (user.alias !== settings.guest) exit();
 if (!settings.user_registration) exit();
-- 
GitLab


From 9bf895677aa461cb1f315b97daba7bcc2c746b19 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 31 Jul 2019 23:58:22 -0400
Subject: [PATCH 510/752] Super duper enhancement.

---
 web/lib/events/sbbsimsg.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/events/sbbsimsg.js b/web/lib/events/sbbsimsg.js
index 821dfaf4bf..e6aba16afd 100644
--- a/web/lib/events/sbbsimsg.js
+++ b/web/lib/events/sbbsimsg.js
@@ -21,7 +21,7 @@ function list() {
 function cycle() {
     if (time() - last_run <= frequency) return;
     last_run = time();
-    list();
+    // list();
 }
 
 this;
-- 
GitLab


From edee9814a83d191c92dee6c6a58036b14f1be195 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 13 Aug 2019 22:41:15 -0400
Subject: [PATCH 511/752] Get angry if someone's alias or realname doesn't pass
 system.check_name(). Stop people named Phil from doing the sorts of things
 that Phils do.

---
 web/root/api/register.ssjs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index fb3641ea6f..05df075649 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -104,7 +104,8 @@ if (system.newuser_password !== '' &&
 
 if (!paramExists('alias') ||
 	paramLength('alias') < MIN_ALIAS ||
-	paramLength('alias') > LEN_ALIAS
+	paramLength('alias') > LEN_ALIAS ||
+	!system.check_name(http_request.query.alias[0])
 ) {
 	reply.errors.push(locale.strings.api_register.error_invalid_alias);
 } else if (system.matchuser(http_request.query.alias[0]) > 0) {
@@ -147,7 +148,8 @@ if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
 if (required(UQ_REALNAME) &&
 	(	!paramExists('realname') ||
 		paramLength('realname') < MIN_REALNAME ||
-		paramLength('realname') > LEN_NAME
+		paramLength('realname') > LEN_NAME ||
+		!system.check_name(http_request.query.alias[0])
 	)
 ) {
 	reply.errors.push(locale.strings.api_register.error_invalid_name);
-- 
GitLab


From 7af740bc2d48a8fc3eca315dc249ef1ffff893fa Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 13 Aug 2019 22:55:21 -0400
Subject: [PATCH 512/752] Let Hugh Jass and Mike Litoris sign up if they want
 to.

---
 web/root/api/register.ssjs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 05df075649..498636a168 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -148,8 +148,7 @@ if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
 if (required(UQ_REALNAME) &&
 	(	!paramExists('realname') ||
 		paramLength('realname') < MIN_REALNAME ||
-		paramLength('realname') > LEN_NAME ||
-		!system.check_name(http_request.query.alias[0])
+		paramLength('realname') > LEN_NAME
 	)
 ) {
 	reply.errors.push(locale.strings.api_register.error_invalid_name);
-- 
GitLab


From 002e1d0b860d1a6db520678576bd80abd0bb4640 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 14 Aug 2019 07:54:03 -0400
Subject: [PATCH 513/752] Sorry, Mr. Litoris - you're not welcome here after
 all.

---
 web/root/api/register.ssjs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 498636a168..05df075649 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -148,7 +148,8 @@ if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
 if (required(UQ_REALNAME) &&
 	(	!paramExists('realname') ||
 		paramLength('realname') < MIN_REALNAME ||
-		paramLength('realname') > LEN_NAME
+		paramLength('realname') > LEN_NAME ||
+		!system.check_name(http_request.query.alias[0])
 	)
 ) {
 	reply.errors.push(locale.strings.api_register.error_invalid_name);
-- 
GitLab


From f65a4e99e1ee1af2b0458e0571f734206d767ba2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Aug 2019 17:25:29 -0400
Subject: [PATCH 514/752] Added 'session_start' value to user's session store.
 This is updated on new logins, or after settings.timeout expires. (This will
 also be the place to update the recent-logins list.)

---
 web/lib/auth.js | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index ac80ef7952..692047d8b3 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -48,7 +48,7 @@ function setCookie(usr, sessionKey) {
 
 function validateSession(cookies) {
 
-  var usr = new User(0);
+	var usr = new User(0);
 	for (var c in cookies) {
 
 		if (cookies[c].search(/^\d+,\w+$/) < 0) continue;
@@ -72,13 +72,16 @@ function validateSession(cookies) {
 		}
 
 		var _usr = authenticate(usr.alias, usr.security.password);
-    _usr = undefined;
+		_usr = undefined;
 		setCookie(usr, session.key);
 		setSessionValue(usr.number, 'ip_address', client.ip_address);
+		if (session.session_start === undefined || time() - parseInt(session.session_start, 10) > settings.timeout) {
+			setSessionValue(usr.number, 'session_start', time());
+		}
 		break;
 
 	}
-  usr = undefined;
+	usr = undefined;
 
 }
 
@@ -161,12 +164,10 @@ function is_user() {
     	if (usr instanceof User) setCookie(usr, randomString(512));
     // If they have a cookie
     } else if(
-    	typeof http_request.cookie.synchronet !== 'undefined' &&
-    	http_request.cookie.synchronet.some(
-    		function (e) {
-    		 	return(e.search(/^\d+,\w+$/) != -1);
-    		}
-    	)
+		typeof http_request.cookie.synchronet !== 'undefined' &&
+		http_request.cookie.synchronet.some(function (e) {
+			return(e.search(/^\d+,\w+$/) != -1);
+		})
     ) {
     	// Verify & update their session, or log them out if requested
     	if (typeof http_request.query.logout === 'undefined') {
-- 
GitLab


From 554d7b427969269f3b0e02a5ea3c522ac86e6415 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Aug 2019 23:11:52 -0400
Subject: [PATCH 515/752] Use logonlist_lib to add an entry on login / renewal
 of old session. If user is logging in (sent credentials and successfully
 authed), destroy their existing session store (if any) to treat this as a new
 login.

---
 web/lib/auth.js | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 692047d8b3..f4590e198a 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -77,6 +77,8 @@ function validateSession(cookies) {
 		setSessionValue(usr.number, 'ip_address', client.ip_address);
 		if (session.session_start === undefined || time() - parseInt(session.session_start, 10) > settings.timeout) {
 			setSessionValue(usr.number, 'session_start', time());
+			const logonlist_lib = load({}, 'logonlist_lib.js');
+			logonlist_lib.add({ node: 'W' });
 		}
 		break;
 
@@ -152,19 +154,23 @@ function is_user() {
 
 (function () {
     // If someone is trying to log in
-    if (typeof http_request.query.username !== 'undefined' &&
+    if (http_request.query.username !== undefined &&
     	http_request.query.username[0].length <= LEN_ALIAS &&
-    	typeof http_request.query.password != 'undefined' &&
+    	http_request.query.password !== undefined &&
     	http_request.query.password[0].length <= LEN_PASS
     ) {
     	var usr = authenticate(
     		http_request.query.username[0],
     		http_request.query.password[0]
     	);
-    	if (usr instanceof User) setCookie(usr, randomString(512));
+		if (usr instanceof User) {
+			destroySession(http_request.cookie.synchronet || {});
+			setCookie(usr, randomString(512));
+		}
+		usr = undefined;
     // If they have a cookie
-    } else if(
-		typeof http_request.cookie.synchronet !== 'undefined' &&
+    } else if (
+		http_request.cookie.synchronet !== undefined &&
 		http_request.cookie.synchronet.some(function (e) {
 			return(e.search(/^\d+,\w+$/) != -1);
 		})
-- 
GitLab


From ab1ee6ef66cc775d395112634332fe778bfc56fe Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Aug 2019 23:43:02 -0400
Subject: [PATCH 516/752] First pass at "last few callers" sidebar module.
 Since you don't "call" a website, we'll just call this a "recent visitors"
 list. Visitation may occur via H.T.T.P. or T.E.L.N.E.T. etc.

---
 web/sidebar/.examples/002-recent-visitors.xjs | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 web/sidebar/.examples/002-recent-visitors.xjs

diff --git a/web/sidebar/.examples/002-recent-visitors.xjs b/web/sidebar/.examples/002-recent-visitors.xjs
new file mode 100644
index 0000000000..492dfc5b29
--- /dev/null
+++ b/web/sidebar/.examples/002-recent-visitors.xjs
@@ -0,0 +1,17 @@
+<?xjs const logonlist_lib = load({}, 'logonlist_lib.js'); ?>
+
+<h4>Recent Visitors</h4>
+<ul class="list-group">
+    <?xjs logonlist_lib.get(0).forEach(function (e) { ?>
+        <li class="list-group-item striped">
+            <strong><? write(e.user.alias); ?></strong>
+            <br>
+            <em><? write(new Date(e.time * 1000).toLocaleString()); ?></em>
+            <br>
+            <?xjs if (e.user.location != '') { ?>
+                from <strong><? write(e.user.location); ?></strong>
+            <?xjs } ?>
+            via <strong><? write(e.user.connection); ?></strong>
+        </li>
+    <?xjs }); ?>
+</ul>
\ No newline at end of file
-- 
GitLab


From b94ab71dbd58b1637d65674445f9034623509e7d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 15 Aug 2019 23:56:47 -0400
Subject: [PATCH 517/752] Pass the name of our fakenode as 'Web' instead of
 'W'.

---
 web/lib/auth.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index f4590e198a..7f856f893d 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -78,7 +78,7 @@ function validateSession(cookies) {
 		if (session.session_start === undefined || time() - parseInt(session.session_start, 10) > settings.timeout) {
 			setSessionValue(usr.number, 'session_start', time());
 			const logonlist_lib = load({}, 'logonlist_lib.js');
-			logonlist_lib.add({ node: 'W' });
+			logonlist_lib.add({ node: 'Web' });
 		}
 		break;
 
-- 
GitLab


From 9e6382cdd0e31d7e22d8092523448e6ffb1e9983 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 16 Aug 2019 00:05:32 -0400
Subject: [PATCH 518/752] Add (and use) localization strings for the Recent
 Visitors sidebar module.

---
 web/lib/locale/en_us.ini                      | 5 +++++
 web/sidebar/.examples/002-recent-visitors.xjs | 8 +++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/web/lib/locale/en_us.ini b/web/lib/locale/en_us.ini
index 66ca16e6c6..94be526bf2 100644
--- a/web/lib/locale/en_us.ini
+++ b/web/lib/locale/en_us.ini
@@ -99,6 +99,11 @@ label_send_telegram = Send a telegram
 label_status_column = Status
 label_status_web = browsing
 
+[sidebar_recent_visitors]
+label_title = Recent Visitors
+label_location = from
+label_connection = via
+
 [sidebar_system_info]
 label_title = System Info
 label_sysop = Sysop:
diff --git a/web/sidebar/.examples/002-recent-visitors.xjs b/web/sidebar/.examples/002-recent-visitors.xjs
index 492dfc5b29..939a4fa5bb 100644
--- a/web/sidebar/.examples/002-recent-visitors.xjs
+++ b/web/sidebar/.examples/002-recent-visitors.xjs
@@ -1,6 +1,6 @@
 <?xjs const logonlist_lib = load({}, 'logonlist_lib.js'); ?>
 
-<h4>Recent Visitors</h4>
+<h4><? locale.write('label_title', 'sidebar_recent_visitors'); ?></h4>
 <ul class="list-group">
     <?xjs logonlist_lib.get(0).forEach(function (e) { ?>
         <li class="list-group-item striped">
@@ -9,9 +9,11 @@
             <em><? write(new Date(e.time * 1000).toLocaleString()); ?></em>
             <br>
             <?xjs if (e.user.location != '') { ?>
-                from <strong><? write(e.user.location); ?></strong>
+                <? locale.write('label_location', 'sidebar_recent_visitors'); ?>
+                <strong><? write(e.user.location); ?></strong>
             <?xjs } ?>
-            via <strong><? write(e.user.connection); ?></strong>
+            <? locale.write('label_connection', 'sidebar_recent_visitors'); ?>
+            <strong><? write(e.user.connection); ?></strong>
         </li>
     <?xjs }); ?>
 </ul>
\ No newline at end of file
-- 
GitLab


From d502c325b7e9960362aa834eea50c683cfcdc420 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 16 Aug 2019 00:13:48 -0400
Subject: [PATCH 519/752] Load logonlist settings from modopts.ini. Use
 last_few_callers setting of above to determine how many records to display.
 Reverse the order of the list; for whatever reason, that looks better to me
 right now.

---
 web/sidebar/.examples/002-recent-visitors.xjs | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/web/sidebar/.examples/002-recent-visitors.xjs b/web/sidebar/.examples/002-recent-visitors.xjs
index 939a4fa5bb..65bb4a070d 100644
--- a/web/sidebar/.examples/002-recent-visitors.xjs
+++ b/web/sidebar/.examples/002-recent-visitors.xjs
@@ -1,8 +1,13 @@
-<?xjs const logonlist_lib = load({}, 'logonlist_lib.js'); ?>
+<?xjs
+    const logonlist_lib = load({}, 'logonlist_lib.js');
+    var options = load("modopts.js", "logonlist");
+    if  (!options) options = {};
+    if (options.last_few_callers === undefined) options.last_few_callers = 4;
+?>
 
 <h4><? locale.write('label_title', 'sidebar_recent_visitors'); ?></h4>
 <ul class="list-group">
-    <?xjs logonlist_lib.get(0).forEach(function (e) { ?>
+    <?xjs logonlist_lib.get(options.last_few_callers).reverse().forEach(function (e) { ?>
         <li class="list-group-item striped">
             <strong><? write(e.user.alias); ?></strong>
             <br>
-- 
GitLab


From 4351f69b431ed7fe2ecf72106ce248535e6a4d8a Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 16 Aug 2019 00:18:24 -0400
Subject: [PATCH 520/752] Tidy up. Get last callers with negative index for
 most recent calls.

---
 web/sidebar/.examples/002-recent-visitors.xjs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/web/sidebar/.examples/002-recent-visitors.xjs b/web/sidebar/.examples/002-recent-visitors.xjs
index 65bb4a070d..b96c903828 100644
--- a/web/sidebar/.examples/002-recent-visitors.xjs
+++ b/web/sidebar/.examples/002-recent-visitors.xjs
@@ -1,13 +1,12 @@
 <?xjs
     const logonlist_lib = load({}, 'logonlist_lib.js');
-    var options = load("modopts.js", "logonlist");
-    if  (!options) options = {};
+    const options = load("modopts.js", "logonlist") || {};
     if (options.last_few_callers === undefined) options.last_few_callers = 4;
 ?>
 
 <h4><? locale.write('label_title', 'sidebar_recent_visitors'); ?></h4>
 <ul class="list-group">
-    <?xjs logonlist_lib.get(options.last_few_callers).reverse().forEach(function (e) { ?>
+    <?xjs logonlist_lib.get(-options.last_few_callers).reverse().forEach(function (e) { ?>
         <li class="list-group-item striped">
             <strong><? write(e.user.alias); ?></strong>
             <br>
-- 
GitLab


From a04716223ff3504a245613a3b503aeee76b40561 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 16 Aug 2019 10:20:47 -0400
Subject: [PATCH 521/752] Hide sidebar items with no content.

---
 web/root/css/style.css | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/web/root/css/style.css b/web/root/css/style.css
index 16281fe0bc..7751bb1a8e 100644
--- a/web/root/css/style.css
+++ b/web/root/css/style.css
@@ -77,8 +77,12 @@ pre.list {
 }
 
 .list-group-item.sidebar {
-	overflow : hidden;
-	word-wrap : break-word;
+	overflow: hidden;
+	word-wrap: break-word;
+}
+
+.list-group-item.sidebar:empty {
+	display: none;
 }
 
 .icon {
-- 
GitLab


From 221ea2ff53f48b514e0db33bc51114049f9b76fb Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 16 Aug 2019 10:23:36 -0400
Subject: [PATCH 522/752] Some tidying up of old var usage and unused things.
 For txt sidebar modules, avoid outputting <pre> tags unless the file gots
 some stuff in it.

---
 web/lib/sidebar.js | 39 ++++++++++++++++++---------------------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/web/lib/sidebar.js b/web/lib/sidebar.js
index 5550bc28d9..92b0ba53ed 100644
--- a/web/lib/sidebar.js
+++ b/web/lib/sidebar.js
@@ -1,24 +1,22 @@
 function getFileContents(file) {
-	var ret = '';
-	var f = new File(file);
-	if (f.open('r')) {
-		ret = f.read();
-		f.close();
-	}
+	const f = new File(file);
+	if (!f.open('r')) return '';
+	const ret = f.read();
+	f.close();
 	return ret;
 }
 
 function getSidebarModules() {
-	return directory(settings.web_sidebar + '*').filter(
-		function (e, i, a) { return (!file_isdir(e)); }
-	);
+	return directory(settings.web_sidebar + '*').filter(function (e) {
+		return (!file_isdir(e));
+	});
 }
 
 function getSidebarModule(module) {
 
 	var ret = '';
 	if (!file_exists(module)) return ret;
-	var ext = file_getext(module).toUpperCase();
+	const ext = file_getext(module).toUpperCase();
 
 	switch (ext) {
 		case '.SSJS':
@@ -36,7 +34,8 @@ function getSidebarModule(module) {
 			ret = getFileContents(module);
 			break;
 		case '.TXT':
-			ret = '<pre>' + getFileContents(module) + '</pre>';
+			const tfc = getFileContents(module);
+			if (tfc.length) ret = '<pre>' + tfc + '</pre>';
 			break;
 		default:
 			break;
@@ -47,16 +46,14 @@ function getSidebarModule(module) {
 }
 
 function writeSidebarModules() {
-	var modules = getSidebarModules();
+	const modules = getSidebarModules();
 	write('<ul class="list-group">');
-	modules.forEach(
-		function (module) {
-			if (module.search(/\.xjs\.ssjs$/i) >= 0) return;
-			write('<li class="list-group-item sidebar">');
-			var str = getSidebarModule(module);
-			if (str !== '') write(str);
-			write('</li>');
-		}
-	);
+	modules.forEach(function (module) {
+		if (module.search(/\.xjs\.ssjs$/i) >= 0) return;
+		write('<li class="list-group-item sidebar">');
+		const str = getSidebarModule(module);
+		if (str.length) write(str);
+		write('</li>');
+	});
 	write('</ul>');
 }
-- 
GitLab


From bb4aac1073b09f6077589899d0d3b8847a150185 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 16 Aug 2019 10:26:15 -0400
Subject: [PATCH 523/752] Output nothing unless the list of recent visitors is
 an array.

---
 web/sidebar/.examples/002-recent-visitors.xjs | 38 ++++++++++---------
 1 file changed, 21 insertions(+), 17 deletions(-)

diff --git a/web/sidebar/.examples/002-recent-visitors.xjs b/web/sidebar/.examples/002-recent-visitors.xjs
index b96c903828..26177f8831 100644
--- a/web/sidebar/.examples/002-recent-visitors.xjs
+++ b/web/sidebar/.examples/002-recent-visitors.xjs
@@ -2,22 +2,26 @@
     const logonlist_lib = load({}, 'logonlist_lib.js');
     const options = load("modopts.js", "logonlist") || {};
     if (options.last_few_callers === undefined) options.last_few_callers = 4;
+    const ll = logonlist_lib.get(-options.last_few_callers);
+    if (Array.isArray(ll)) {
 ?>
 
-<h4><? locale.write('label_title', 'sidebar_recent_visitors'); ?></h4>
-<ul class="list-group">
-    <?xjs logonlist_lib.get(-options.last_few_callers).reverse().forEach(function (e) { ?>
-        <li class="list-group-item striped">
-            <strong><? write(e.user.alias); ?></strong>
-            <br>
-            <em><? write(new Date(e.time * 1000).toLocaleString()); ?></em>
-            <br>
-            <?xjs if (e.user.location != '') { ?>
-                <? locale.write('label_location', 'sidebar_recent_visitors'); ?>
-                <strong><? write(e.user.location); ?></strong>
-            <?xjs } ?>
-            <? locale.write('label_connection', 'sidebar_recent_visitors'); ?>
-            <strong><? write(e.user.connection); ?></strong>
-        </li>
-    <?xjs }); ?>
-</ul>
\ No newline at end of file
+    <h4><? locale.write('label_title', 'sidebar_recent_visitors'); ?></h4>
+    <ul class="list-group">
+        <?xjs ll.reverse().forEach(function (e) { ?>
+            <li class="list-group-item striped">
+                <strong><? write(e.user.alias); ?></strong>
+                <br>
+                <em><? write(new Date(e.time * 1000).toLocaleString()); ?></em>
+                <br>
+                <?xjs if (e.user.location != '') { ?>
+                    <? locale.write('label_location', 'sidebar_recent_visitors'); ?>
+                    <strong><? write(e.user.location); ?></strong>
+                <?xjs } ?>
+                <? locale.write('label_connection', 'sidebar_recent_visitors'); ?>
+                <strong><? write(e.user.connection); ?></strong>
+            </li>
+        <?xjs }); ?>
+    </ul>
+
+<?xjs } ?>
-- 
GitLab


From 88f8b7730d2d3c4297d1e81f591199001c59f291 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 17 Aug 2019 00:17:45 -0400
Subject: [PATCH 524/752] Use fetch method instead of jq getJSON. Feed post and
 get requests into a common function, which will become useful later. Starting
 to use ES6 stuff in client-side scripts with greater abandon.  May not work
 with your ancient browser. #wontfix

---
 web/root/js/common.js | 65 +++++++++++++++++++++++++++----------------
 1 file changed, 41 insertions(+), 24 deletions(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index f7812e70a6..7dcb27158a 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -2,35 +2,53 @@
 var updateInterval = 60000;
 const _sbbs_events = {};
 
-function login(evt) {
+async function v4_fetch(url, method, body) {
+	const init = { method, headers: {} };
+	if (method == 'POST' && body) {
+		init.body = body;
+		if (body instanceof URLSearchParams) {
+			init.headers['Content-Type'] = 'application/x-www-form-urlencoded';
+		}
+	}
+	try {
+		return await fetch(url, init).then(res => res.json());
+	} catch (err) {
+		console.log('Error on fetch', url, init);
+	}
+}
+
+function v4_get(url) {
+	return v4_fetch(url);
+}
+
+// May need adjustment for multipart/form-data at some point
+function v4_post(url, data) {
+	const fd = new URLSearchParams();
+	for (let e in data) {
+		fd.append(e, data[e]);
+	}
+	return v4_fetch(url, 'POST', fd);
+}
+
+async function login(evt) {
 	if ($('#input-username').val() == '' || $('#input-password').val() == '') {
 		return;
 	}
 	if (typeof evt !== 'undefined') evt.preventDefault();
-	$.ajax({
-        url: './api/auth.ssjs',
-		method: 'POST',
-		data: {
-			username : $('#input-username').val(),
-			password : $('#input-password').val()
-		}
-	}).done(function (data) {
-		if (data.authenticated) {
-			window.location.reload(true);
-		} else {
-			$('#login-form').append('<p class="text-danger">Login failed</p>');
-		}
+	const res = await v4_post('./api/auth.ssjs', {
+		username: $('#input-username').val(),
+		password: $('#input-password').val()
 	});
+	if (res.authenticated) {
+		window.location.reload(true);
+	} else {
+		$('#login-form').append('<p class="text-danger">Login failed</p>');
+	}
 }
 
-function logout() {
-	$.ajax({
-        url: './api/auth.ssjs',
-		method: 'GET',
-		data: { logout: true }
-	}).done(function (data) {
-        if (!data.authenticated) window.location.href = '/';
-    });
+async function logout() {
+	const res = await v4_get('./api/auth.ssjs?logout=true');
+	if (!res.authenticated) window.location.href = '/';
 }
 
 function scrollUp() {
@@ -42,8 +60,7 @@ function scrollUp() {
 function sendTelegram(alias) {
     function send_tg(evt) {
         if (typeof evt !== 'undefined') evt.preventDefault();
-        $.getJSON('./api/system.ssjs?call=send-telegram&user=' + alias + '&telegram=' + $('#telegram').val(), function(data) {
-        });
+		v4_post('./api/system.ssjs', { call: 'send-telegram', user: alias, telegram: $('#telegram').val() });
         $('#popUpModal').modal('hide');
     }
 	$('#popUpModalTitle').html('Send a telegram to ' + alias);
-- 
GitLab


From 3aa29ef7f491bb9a7a84e465b0fa0ba4922f06a2 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 17 Aug 2019 00:24:36 -0400
Subject: [PATCH 525/752] POST logout.

---
 web/root/js/common.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index 7dcb27158a..a56d75925a 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -47,7 +47,7 @@ async function login(evt) {
 }
 
 async function logout() {
-	const res = await v4_get('./api/auth.ssjs?logout=true');
+	const res = await v4_post('./api/auth.ssjs', { logout: true });
 	if (!res.authenticated) window.location.href = '/';
 }
 
-- 
GitLab


From 5aede98f006d4d05bdbde636d0c69fd7a11131a6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 17 Aug 2019 00:26:42 -0400
Subject: [PATCH 526/752] Use newer fetch methods instead of $.getJSON().

---
 web/pages/.examples/000-mail.xjs       | 13 +++++--------
 web/pages/.examples/003-games.xjs      |  9 ++++-----
 web/sidebar/.examples/001-nodelist.xjs | 16 +++++++---------
 3 files changed, 16 insertions(+), 22 deletions(-)

diff --git a/web/pages/.examples/000-mail.xjs b/web/pages/.examples/000-mail.xjs
index 6f767e8347..7c1b9aada3 100644
--- a/web/pages/.examples/000-mail.xjs
+++ b/web/pages/.examples/000-mail.xjs
@@ -62,17 +62,14 @@
         });
     }
 
-    function deleteSelectedMail() {
-        var numbers = [];
+    async function deleteSelectedMail() {
+        const numbers = [];
         $('input.mail-select:checked').each(function () {
             numbers.push($(this).attr('id').split('-')[1]);
         });
-        $.getJSON('./api/forum.ssjs?call=delete-mail&number=' + numbers.join('&number='), function (data) {
-            if (!data.success) return;
-            numbers.forEach(function (e) {
-                $('#li-' + e).remove();
-            });
-        });
+        const data = await v4_get('./api/forum.ssjs?call=delete-mail&number=' + numbers.join('&number='));
+        if (!data.success) return;
+        numbers.forEach(e => $('#li-' + e).remove());
     }
 
 </script>
diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index 6c82640e70..41e73bdaf1 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -67,11 +67,10 @@
         window.location.reload();
     };
 
-    function launchXtrn(code) {
-        $.getJSON('./api/system.ssjs?call=set-xtrn-intent&code=' + code, function(data) {
-            fTelnet._Options.RLoginTerminalType = 'xtrn=' + code;
-            fTelnet.Connect();
-        });
+    async function launchXtrn(code) {
+        await v4_get('./api/system.ssjs?call=set-xtrn-intent&code=' + code);
+        fTelnet._Options.RLoginTerminalType = 'xtrn=' + code;
+        fTelnet.Connect();
     }
 
     var xtrn = <?xjs write(JSON.stringify(xtrn)); ?>;
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/web/sidebar/.examples/001-nodelist.xjs
index 395728f42f..ed922ea45b 100644
--- a/web/sidebar/.examples/001-nodelist.xjs
+++ b/web/sidebar/.examples/001-nodelist.xjs
@@ -84,16 +84,14 @@
 
     <?xjs if (settings.nodelist_ibbs) { ?>
         function _send_ibbs_telegram(sys, host, user) {
-            function send_ibbs_tg(evt) {
+            async function send_ibbs_tg(evt) {
                 if (typeof evt !== 'undefined') evt.preventDefault();
-                $.getJSON(
-                    './api/sbbsimsg.ssjs?call=send_telegram'
-                    + '&username=' + user
-                    + '&host=' + host
-                    + '&message=' + $('#telegram').val(),
-                    function (data) {
-                    }
-        		);
+                await v4_post('./api/sbbsimsg.ssjs', {
+                    call: 'send_telegram',
+                    username: user,
+                    host: host,
+                    message: $('#telegram').val()
+                });
                 $('#popUpModal').modal('hide');
         	};
             $('#popUpModalTitle').html('Send a telegram to ' + user + '@' + sys);
-- 
GitLab


From 74e89c8853879aeaecadccbab2a5fbcf1d27dcf4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 17 Aug 2019 01:04:07 -0400
Subject: [PATCH 527/752] Allow a post data field to be an array, append
 multiple.

---
 web/root/js/common.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/web/root/js/common.js b/web/root/js/common.js
index a56d75925a..b922962687 100644
--- a/web/root/js/common.js
+++ b/web/root/js/common.js
@@ -25,7 +25,11 @@ function v4_get(url) {
 function v4_post(url, data) {
 	const fd = new URLSearchParams();
 	for (let e in data) {
-		fd.append(e, data[e]);
+		if (Array.isArray(data[e])) {
+			data[e].forEach(ee => fd.append(e, ee));
+		} else {
+			fd.append(e, data[e]);
+		}
 	}
 	return v4_fetch(url, 'POST', fd);
 }
-- 
GitLab


From 98d78bc33a2bca272abdceb9384d438e9b61f437 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Sat, 17 Aug 2019 01:04:25 -0400
Subject: [PATCH 528/752] Replace all ajax/getJSON calls with newer fetch
 methods. Use get/post as appropriate. Light housekeeping; hopefully this
 script won't live much longer.

---
 web/root/js/forum.js | 334 ++++++++++++++++++++-----------------------
 1 file changed, 156 insertions(+), 178 deletions(-)

diff --git a/web/root/js/forum.js b/web/root/js/forum.js
index 35ce37e738..679dd5a97f 100644
--- a/web/root/js/forum.js
+++ b/web/root/js/forum.js
@@ -30,63 +30,51 @@ function quotify(id) {
 }
 
 // (Try to) post a new message to 'sub' via the web API
-function postNew(sub) {
+async function postNew(sub) {
 	$('#newmessage-button').attr('disabled', true);
 	var to = $('#newmessage-to').val();
 	var subject = $('#newmessage-subject').val();
 	var body = $('#newmessage-body').val();
-	$.ajax({
-        url: './api/forum.ssjs',
-		method: 'POST',
-		data: {
-			call: 'post',
-			sub: sub,
-			to: to,
-			subject: subject,
-			body: body
-		}
-	}).done(function (data) {
-		if (data.success) {
-			$('#newmessage').remove();
-			insertParam('notice', 'Your message has been posted.');
-		}
-		$('#newmessage-button').attr('disabled', false);
+	const data = await v4_post('./api/forum.ssjs', {
+		call: 'post',
+		sub,
+		to,
+		subject,
+		body
 	});
+	if (data.success) {
+		$('#newmessage').remove();
+		insertParam('notice', 'Your message has been posted.');
+	}
+	$('#newmessage-button').attr('disabled', false);
 }
 
 // (Try to) post a reply to message number 'id' of 'sub' via the web API
-function postReply(sub, id) {
+async function postReply(sub, id) {
 	$('#reply-button-' + id).attr('disabled', true);
 	var body = $('#replytext-' + id).val();
-	$.ajax({
-        url: './api/forum.ssjs',
-		method: 'POST',
-		data: {
-			call: 'post-reply',
-			sub: sub,
-			body: $('#replytext-' + id).val(),
-			pid: id
-		}
-	}).done(function (data) {
-		if (data.success) {
-			$('#quote-' + id).attr('disabled', false);
-			$('#replybox-' + id).remove();
-			insertParam('notice', 'Your message has been posted.');
-		} else {
-			$('#reply-button-' + id).attr('disabled', false);
-		}
+	const data = await v4_post('./api/forum.ssjs', {
+		call: 'post-reply',
+		sub,
+		body,
+		pid: id
 	});
+	if (data.success) {
+		$('#quote-' + id).attr('disabled', false);
+		$('#replybox-' + id).remove();
+		insertParam('notice', 'Your message has been posted.');
+	} else {
+		$('#reply-button-' + id).attr('disabled', false);
+	}
 }
 
 // (Try to) delete a message via the web API
-function deleteMessage(sub, id) {
-	$.getJSON('./api/forum.ssjs?call=delete-message&sub=' + sub + '&number=' + id, function (data) {
-		console.log(data);
-		if (data.success) {
-			$('#li-' + id).remove();
-			insertParam('notice', 'Message deleted.');
-		}
-	});
+async function deleteMessage(sub, id) {
+	const res = await v4_post('./api/forum.ssjs', { call: 'delete-message', sub: sub, number: id });
+	if (res.success) {
+		$('#li-' + id).remove();
+		insertParam('notice', 'Message deleted.');
+	}
 }
 
 // Add a new message input form to the element with id 'forum-list-container' for sub 'sub'
@@ -100,19 +88,15 @@ function addNew(sub) {
 		'<input id="newmessage-button" class="btn btn-primary" type="submit" value="Submit" onclick="postNew(\'' + sub + '\')">' +
 		'</li>'
 	);
-	$.getJSON('./api/forum.ssjs?call=get-signature', function (data) {
-		$('#newmessage-body').val(
-            $('#newmessage-body').val() + '\r\n' + data.signature
-		);
+	v4_get('./api/forum.ssjs?call=get-signature').then(data => {
+		$('#newmessage-body').val($('#newmessage-body').val() + '\r\n' + data.signature);
         $("#newmessage-body")[0].setSelectionRange(0,0);
 	});
 	window.location.hash = '#newmessage';
-	$('#newmessage-body').keydown(function (evt) {
-        evt.stopImmediatePropagation();
-    });
+	$('#newmessage-body').keydown(evt => evt.stopImmediatePropagation());
 }
 
-function submitPoll(sub) {
+async function submitPoll(sub) {
 
 	$('#newpoll-submit').attr('disabled', true);
 
@@ -141,23 +125,20 @@ function submitPoll(sub) {
 		if (val !== '') comments.push(val);
 	});
 
-	var url =
-		'./api/forum.ssjs?call=submit-poll' +
-		'&sub=' + sub +
-		'&subject=' + subject +
-		'&votes=' + answerCount +
-		'&results=' + results +
-		'&answer=' + answers.join('&answer=');
-
-	if (comments.length > 0) url += '&comment=' + comments.join('&comment=');
-
-	$.getJSON(url, function (data) {
-		$('#newpoll-submit').attr('disabled', false);
-		if (data.success) {
-			$('#newpoll').remove();
-			insertParam('notice', 'Your poll has been posted.');
-		}
-	});
+	const post_data = {
+		sub,
+		subject,
+		votes: answerCount,
+		results,
+		answer: answers
+	};
+	if (comments.length) post_data.comment = comments;
+	const res = await v4_post('./api/forum.ssjs?call=submit-poll', post_data);
+	$('#newpoll-submit').attr('disabled', false);
+	if (res.success) {
+		$('#newpoll').remove();
+		insertParam('notice', 'Your poll has been posted.');
+	}
 
 }
 
@@ -269,14 +250,11 @@ function addReply(sub, id) {
 		'<input id="reply-button-' + id + '" class="btn btn-primary" type="submit" value="Submit" onclick="postReply(\'' + sub + '\', ' + id + ')">' +
 		'</div>'
 	);
-	$.getJSON('./api/forum.ssjs?call=get-signature', function (data) {
+	v4_get('./api/forum.ssjs?call=get-signature').then(data => {
 		$('#replytext-' + id).val($('#replytext-' + id).val() + '\r\n' + data.signature);
         $('#replytext-' + id)[0].setSelectionRange(0,0);
-
-	});
-	$('#replytext-' + id).keydown(function (evt) {
-		evt.stopImmediatePropagation();
 	});
+	$('#replytext-' + id).keydown(evt => evt.stopImmediatePropagation());
 }
 
 function onSubUnreadCount(data) {
@@ -292,8 +270,9 @@ function onSubUnreadCount(data) {
 }
 
 // 'sub' can be a single sub code, or a string of <sub1>&sub=<sub2>&sub=<sub3>...
-function getSubUnreadCount(sub) {
-	$.getJSON('./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub, onSubUnreadCount);
+async function getSubUnreadCount(sub) {
+	const res = await v4_get('./api/forum.ssjs?call=get-sub-unread-count&sub=' + sub);
+	onSubUnreadCount(res);
 }
 
 function onGroupUnreadCount(data) {
@@ -302,18 +281,17 @@ function onGroupUnreadCount(data) {
 			(data[group].scanned == 0 ? "" : data[group].scanned)
 		);
 		$('#badge-ignored-' + group).text(
-			(	data[group].total == 0 ||
-				data[group].total == data[group].scanned
-				? ''
-				: (data[group].total - data[group].scanned)
-			)
+			data[group].total == 0 || data[group].total == data[group].scanned
+			? ''
+			: (data[group].total - data[group].scanned)
 		);
 	}
 }
 
 // 'group' can be a single group index, or a string of 0&group=1&group=2...
-function getGroupUnreadCount(group) {
-	$.getJSON('./api/forum.ssjs?call=get-group-unread-count&group=' + group, onGroupUnreadCount);
+async function getGroupUnreadCount(group) {
+	const res = await v4_get('./api/forum.ssjs?call=get-group-unread-count&group=' + group);
+	onGroupUnreadCount(res);
 }
 
 function onThreadStats(data) {
@@ -373,46 +351,44 @@ function onThreadStats(data) {
 
 /*  Fetch a private mail message's body (with links to attachments) where 'id'
 	is the message number.	Output it to an element with id 'message-<id>'. */
-function getMailBody(id) {
+async function getMailBody(id) {
 	if (!$('#message-' + id).attr('hidden')) {
 		$('#message-' + id).attr('hidden', true);
 	} else if ($('#message-' + id).html() != '') {
 		$('#message-' + id).attr('hidden', false);
 	} else {
-		$.getJSON('./api/forum.ssjs?call=get-mail-body&number=' + id, function (data) {
-			var str = data.body;
-			if (data.inlines && data.inlines.length > 0) {
-				str += '<br>Inline attachments: ' + data.inlines.join('<br>') + '<br>';
-			}
-			if (data.attachments && data.attachments.length > 0) {
-				str += '<br>Attachments: ' + data.attachments.join('<br>') + '<br>';
-			}
-			str +=
-				'<button class="btn btn-default icon" ' +
-				'aria-label="Reply to this message" ' +
-				'title="Reply to this message" ' +
-				'name="reply-' + id + '" ' +
-				'onclick="addReply(\'mail\',' + id + ')">' +
-				'<span class="glyphicon glyphicon-comment"></span>' +
-				'</button>' +
-				'<button class="btn btn-default icon" aria-label="Delete this message" ' +
-				'title="Delete this message" onclick="deleteMessage(\'mail\',' + id + ')">' +
-				'<span class="glyphicon glyphicon-trash"></span>' +
-				'</button>';
-			$('#message-' + id).html(str);
-			$('#message-' + id).attr('hidden', false);
-		});
+		const data = await v4_get('./api/forum.ssjs?call=get-mail-body&number=' + id);
+		var str = data.body;
+		if (data.inlines && data.inlines.length > 0) {
+			str += '<br>Inline attachments: ' + data.inlines.join('<br>') + '<br>';
+		}
+		if (data.attachments && data.attachments.length > 0) {
+			str += '<br>Attachments: ' + data.attachments.join('<br>') + '<br>';
+		}
+		str +=
+			'<button class="btn btn-default icon" ' +
+			'aria-label="Reply to this message" ' +
+			'title="Reply to this message" ' +
+			'name="reply-' + id + '" ' +
+			'onclick="addReply(\'mail\',' + id + ')">' +
+			'<span class="glyphicon glyphicon-comment"></span>' +
+			'</button>' +
+			'<button class="btn btn-default icon" aria-label="Delete this message" ' +
+			'title="Delete this message" onclick="deleteMessage(\'mail\',' + id + ')">' +
+			'<span class="glyphicon glyphicon-trash"></span>' +
+			'</button>';
+		$('#message-' + id).html(str);
+		$('#message-' + id).attr('hidden', false);
 	}
 }
 
-function setScanCfg(sub, cfg) {
+async function setScanCfg(sub, cfg) {
 	var opts = [ 'scan-cfg-off', 'scan-cfg-new', 'scan-cfg-youonly' ];
-	$.getJSON('./api/forum.ssjs?call=set-scan-cfg&sub=' + sub + '&cfg=' + cfg, function (data) {
-		if (!data.success) return;
-		opts.forEach(function (opt, index) {
-			$('#' + opt).toggleClass('btn-primary', (cfg == index));
-			$('#' + opt).toggleClass('btn-default', (cfg != index));
-		});
+	const data = await v4_get('./api/forum.ssjs?call=set-scan-cfg&sub=' + sub + '&cfg=' + cfg);
+	if (!data.success) return;
+	opts.forEach((e, i) => {
+		$('#' + e).toggleClass('btn-primary', (cfg == i));
+		$('#' + e).toggleClass('btn-default', (cfg != i));
 	});
 }
 
@@ -449,19 +425,18 @@ function threadNav() {
 
 }
 
-function vote(sub, id) {
+async function vote(sub, id) {
 	id = id.split('-');
 	if (id.length != 2 || (id[0] != 'uv' && id[0] != 'dv') || isNaN(parseInt(id[1]))) {
 		return;
 	}
-	$.getJSON('./api/forum.ssjs?call=vote&sub=' + sub + '&id=' + id[1] + '&up=' + (id[0] === 'uv' ? 1 : 0), function (data) {
-		if (!data.success) return;
-		$('#' + id[0] + '-' + id[1]).addClass(id[0] === 'uv' ? 'upvote-fg' : 'downvote-fg');
-		$('#' + id[0] + '-' + id[1]).attr('disabled', true);
-		$('#' + id[0] + '-' + id[1]).blur();
-		var count = parseInt($('#' + id[0] + '-count-' + id[1]).text()) + 1;
-		$('#' + id[0] + '-count-' + id[1]).text(count);
-	});
+	const data = await v4_get('./api/forum.ssjs?call=vote&sub=' + sub + '&id=' + id[1] + '&up=' + (id[0] === 'uv' ? 1 : 0));
+	if (!data.success) return;
+	$('#' + id[0] + '-' + id[1]).addClass(id[0] === 'uv' ? 'upvote-fg' : 'downvote-fg');
+	$('#' + id[0] + '-' + id[1]).attr('disabled', true);
+	$('#' + id[0] + '-' + id[1]).blur();
+	const count = parseInt($('#' + id[0] + '-count-' + id[1]).text()) + 1;
+	$('#' + id[0] + '-count-' + id[1]).text(count);
 }
 
 function enableVoteButtonHandlers(sub) {
@@ -473,64 +448,68 @@ function enableVoteButtonHandlers(sub) {
     });
 }
 
-function getVotesInThread(sub, id) {
-	$.getJSON('./api/forum.ssjs?call=get-thread-votes&sub=' + sub + '&id=' + id, function (data) {
-		Object.keys(data.m).forEach(function (m) {
-			var uv = parseInt($('#uv-count-' + m).text());
-			var dv = parseInt($('#dv-count-' + m).text());
-			if (uv !== data.m[m].u) {
-				$('#uv-count-' + m).text(data.m[m].u);
-				$('#uv-' + m).addClass('indicator');
-			}
-			if (dv !== data.m[m].d) {
-				$('#dv-count-' + m).text(data.m[m].d);
-				$('#dv-' + m).addClass('indicator');
-			}
-			switch (data.m[m].v) {
-				case 1:
-					$('#uv-' + m).addClass('upvote-fg');
-					$('#uv-' + m).attr('disabled', true);
-					$('#dv-' + m).attr('disabled', true);
-					break;
-				case 2:
-					$('#dv-' + m).addClass('downvote-fg');
-					$('#uv-' + m).attr('disabled', true);
-					$('#dv-' + m).attr('disabled', true);
-					break;
-				default:
-					break;
-			}
-		});
+async function getVotesInThread(sub, id) {
+	const data = await v4_get('./api/forum.ssjs?call=get-thread-votes&sub=' + sub + '&id=' + id);
+	Object.keys(data.m).forEach(m => {
+		var uv = parseInt($('#uv-count-' + m).text());
+		var dv = parseInt($('#dv-count-' + m).text());
+		if (uv !== data.m[m].u) {
+			$('#uv-count-' + m).text(data.m[m].u);
+			$('#uv-' + m).addClass('indicator');
+		}
+		if (dv !== data.m[m].d) {
+			$('#dv-count-' + m).text(data.m[m].d);
+			$('#dv-' + m).addClass('indicator');
+		}
+		switch (data.m[m].v) {
+			case 1:
+				$('#uv-' + m).addClass('upvote-fg');
+				$('#uv-' + m).attr('disabled', true);
+				$('#dv-' + m).attr('disabled', true);
+				break;
+			case 2:
+				$('#dv-' + m).addClass('downvote-fg');
+				$('#uv-' + m).attr('disabled', true);
+				$('#dv-' + m).attr('disabled', true);
+				break;
+			default:
+				break;
+		}
 	});
 }
 
-function getVotesInThreads(sub) {
-	$.getJSON('./api/forum.ssjs?call=get-sub-votes&sub=' + sub, function (data) {
-		Object.keys(data).forEach(function (t) {
-			var uv = data[t].p.u + ' / ' + data[t].t.u;
-			var dv = data[t].p.d + ' / ' + data[t].t.d;
-			if (uv !== $('#uv-count-' + t).text()) $('#uv-count-' + t).text(uv);
-			if (dv !== $('#dv-count-' + t).text()) $('#dv-count-' + t).text(dv);
-		});
+async function getVotesInThreads(sub) {
+	const data = await v4_get('./api/forum.ssjs?call=get-sub-votes&sub=' + sub);
+	Object.keys(data).forEach(t => {
+		var uv = data[t].p.u + ' / ' + data[t].t.u;
+		var dv = data[t].p.d + ' / ' + data[t].t.d;
+		if (uv !== $('#uv-count-' + t).text()) $('#uv-count-' + t).text(uv);
+		if (dv !== $('#dv-count-' + t).text()) $('#dv-count-' + t).text(dv);
 	});
 }
 
-function submitPollAnswers(sub, id) {
+async function submitPollAnswers(sub, id) {
 	if ($('input[name="poll-' + id + '"]:checked').length < 1) return;
 	var answers = [];
 	$('input[name="poll-' + id + '"]:checked').each(function () {
         answers.push($(this).val());
     });
 	answers = answers.join('&answer=');
-	$.getJSON('./api/forum.ssjs?call=submit-poll-answers&sub=' + sub + '&id=' + id + '&answer=' + answers, function (data) {
-		$('input[name="poll-' + id + '"]').each(function () {
-			$(this).attr('disabled', true);
-			if ($(this).prop('checked')) {
-                $(this).parent().parent().addClass('upvote-bg');
-			}
-		});
-		$('submit-poll-' + id).attr('disabled', true);
+
+	const post_data = {
+		call: 'submit-poll-answers',
+		sub,
+		id,
+		answer: answers
+	};
+	const data = await v4_post('./api/forum.ssjs', post_data);
+	$('input[name="poll-' + id + '"]').each(function () {
+		$(this).attr('disabled', true);
+		if ($(this).prop('checked')) {
+			$(this).parent().parent().addClass('upvote-bg');
+		}
 	});
+	$('submit-poll-' + id).attr('disabled', true);
 }
 
 function pollControl(id, count) {
@@ -549,16 +528,15 @@ function pollControl(id, count) {
 	});
 }
 
-function getPollData(sub, id) {
-	$.getJSON('./api/forum.ssjs?call=get-poll-results&sub=' + sub + '&id=' + id, function (data) {
-		data.tally.forEach(function (e, i) {
-			if (e > 0) $('#poll-count-' + id + '-' + i).text(e);
-		});
-		if (data.answers > 0) {
-			$('input[name="poll-' + id + '"]').each(function () {
-                $(this).attr('disabled', true);
-            });
-			$('#submit-poll-' + id).attr('disabled', true);
-		}
+async function getPollData(sub, id) {
+	const data = await v4_get('./api/forum.ssjs?call=get-poll-results&sub=' + sub + '&id=' + id);
+	data.tally.forEach((e, i) => {
+		if (e > 0) $('#poll-count-' + id + '-' + i).text(e);
 	});
+	if (data.answers > 0) {
+		$('input[name="poll-' + id + '"]').each(function () {
+			$(this).attr('disabled', true);
+		});
+		$('#submit-poll-' + id).attr('disabled', true);
+	}
 }
-- 
GitLab


From 67028a18e3351bc06a2538d2178d364376424ca6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 21 Aug 2019 13:27:07 -0400
Subject: [PATCH 529/752] Added "header" and "footer" page "components".
 Contents of components/header.xjs (if exists) appear under navbar and above
 page content. Contents of components/footer.xjs (if exists) appear at bottom
 of page (in a <footer/> block). You can use this to add a banner/image to the
 top of your pages, or include some server or client-side JS that runs on
 every page load (without having to modify index.xjs).

---
 web/components/footer.xjs |  1 +
 web/components/header.xjs |  0
 web/root/index.xjs        | 14 ++++++++++++--
 3 files changed, 13 insertions(+), 2 deletions(-)
 create mode 100644 web/components/footer.xjs
 create mode 100644 web/components/header.xjs

diff --git a/web/components/footer.xjs b/web/components/footer.xjs
new file mode 100644
index 0000000000..9549be99a4
--- /dev/null
+++ b/web/components/footer.xjs
@@ -0,0 +1 @@
+<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
diff --git a/web/components/header.xjs b/web/components/header.xjs
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 0c9bd773d6..b7619a1e1e 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -104,6 +104,12 @@
 			</div>
 		</div>
 
+		<?xjs if (file_exists(settings.web_lib + '../components/header.xjs')) {
+			(function () {
+				load(xjs_compile(settings.web_lib + '../components/header.xjs'));
+			})();
+		} ?>
+
 		<nav class="navbar navbar-default navbar-fixed-top">
 			<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
 				<div class="navbar-header">
@@ -193,7 +199,11 @@
 			</div>
 		  	<hr>
 			<footer>
-			<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
+				<?xjs if (file_exists(settings.web_lib + '../components/footer.xjs')) {
+					load(xjs_compile(settings.web_lib + '../components/footer.xjs'));
+				} else { ?>
+					<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
+				<?xjs } ?>
 			</footer>
 		</div>
 
@@ -214,4 +224,4 @@
 
 	</body>
 
-</html>
\ No newline at end of file
+</html>
-- 
GitLab


From 47cff956d4276c1442e43180a939d3194f72f584 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 11:56:26 -0400
Subject: [PATCH 530/752] Don't overwrite non-example components.

---
 mods/webv4-installer.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index c35a166745..130f3809ed 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -217,6 +217,8 @@ copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages', fal
 copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages/.examples', true);
 copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar', false);
 copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar/.examples', true);
+copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components', false);
+copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components/.examples', true);
 
 writeln('Cleaning up ...');
 remove_dir(temp_dir + '/web/pages/.examples');
-- 
GitLab


From 1b87f773cf31eb53939d6129c770b064efeff5f4 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 11:58:14 -0400
Subject: [PATCH 531/752] Eggzamples.

---
 web/components/.examples/footer.xjs | 1 +
 web/components/.examples/header.xjs | 0
 2 files changed, 1 insertion(+)
 create mode 100644 web/components/.examples/footer.xjs
 create mode 100644 web/components/.examples/header.xjs

diff --git a/web/components/.examples/footer.xjs b/web/components/.examples/footer.xjs
new file mode 100644
index 0000000000..9549be99a4
--- /dev/null
+++ b/web/components/.examples/footer.xjs
@@ -0,0 +1 @@
+<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
diff --git a/web/components/.examples/header.xjs b/web/components/.examples/header.xjs
new file mode 100644
index 0000000000..e69de29bb2
-- 
GitLab


From 26ddf8c6ae5942b271578f16dee84bc9c654c8a9 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 11:59:29 -0400
Subject: [PATCH 532/752] Relocateded.

---
 web/components/footer.xjs | 1 -
 web/components/header.xjs | 0
 2 files changed, 1 deletion(-)
 delete mode 100644 web/components/footer.xjs
 delete mode 100644 web/components/header.xjs

diff --git a/web/components/footer.xjs b/web/components/footer.xjs
deleted file mode 100644
index 9549be99a4..0000000000
--- a/web/components/footer.xjs
+++ /dev/null
@@ -1 +0,0 @@
-<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
diff --git a/web/components/header.xjs b/web/components/header.xjs
deleted file mode 100644
index e69de29bb2..0000000000
-- 
GitLab


From a5569549543afdb2f55f64b7f0f36d6abfaa5d58 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:33:06 -0400
Subject: [PATCH 533/752] Comments on stuff to be dealt with later

---
 mods/webv4-installer.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index 130f3809ed..c6630a425b 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -214,11 +214,11 @@ copy_dir_contents(temp_dir + '/mods', system.mods_dir, true);
 copy_dir_contents(temp_dir + '/text', system.text_dir, true);
 copy_dir_contents(temp_dir + '/web', install_dir, true);
 copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages', false);
-copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages/.examples', true);
+copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages/.examples', true); // These ... probably aren't necessary
 copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar', false);
-copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar/.examples', true);
+copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar/.examples', true); // These ... probably aren't necessary
 copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components', false);
-copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components/.examples', true);
+copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components/.examples', true); // These ... probably aren't necessary
 
 writeln('Cleaning up ...');
 remove_dir(temp_dir + '/web/pages/.examples');
-- 
GitLab


From ae78e08cde5d2799da8580aae3b727e57fa31db0 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:33:18 -0400
Subject: [PATCH 534/752] Component stuff

---
 .gitignore | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.gitignore b/.gitignore
index bc50a8c6fd..0667d37d9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,7 @@
 # Ignore non-example sidebar modules
 /web/sidebar/*
 !/web/sidebar/.examples
+
+# Ignore non-example components
+/web/components/*
+!/web/components/.examples
-- 
GitLab


From f12584622b379ec9b80399b3654e2b90f386791f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:42:36 -0400
Subject: [PATCH 535/752] Move the modal (notification pop-up) into its own
 component file.

---
 web/components/.examples/modal.xjs | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 web/components/.examples/modal.xjs

diff --git a/web/components/.examples/modal.xjs b/web/components/.examples/modal.xjs
new file mode 100644
index 0000000000..8651ec483a
--- /dev/null
+++ b/web/components/.examples/modal.xjs
@@ -0,0 +1,20 @@
+<div class="modal" id="popUpModal" tabindex="-1" role="dialog" style="display:none;">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h4 class="modal-title" id="popUpModalTitle">
+                    Pop-Up Thingie
+                </h4>
+            </div>
+            <div class="modal-body" id="popUpModalBody"></div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-default" id="popUpModalCloseButton">
+                    <?xjs write(locale.strings.main.button_close); ?>
+                </button>
+                <button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>
+                    <?xjs write(locale.strings.main.button_submit); ?>
+                </button>
+            </div>
+        </div>
+    </div>
+</div>
\ No newline at end of file
-- 
GitLab


From 70bcc9f7ff57a51176849d6c8e0ffab0ca7508ef Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:43:08 -0400
Subject: [PATCH 536/752] Load modal component.

---
 web/root/index.xjs | 33 ++++++++++-----------------------
 1 file changed, 10 insertions(+), 23 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index b7619a1e1e..fdc03bd281 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -83,32 +83,19 @@
 		<script src="./bootstrap/js/bootstrap.min.js"></script>
 		<script src="./js/common.js"></script>
 
-		<div class="modal" id="popUpModal" tabindex="-1" role="dialog" style="display:none;">
-			<div class="modal-dialog" role="document">
-				<div class="modal-content">
-					<div class="modal-header">
-						<h4 class="modal-title" id="popUpModalTitle">
-							Pop-Up Thingie
-						</h4>
-					</div>
-					<div class="modal-body" id="popUpModalBody"></div>
-					<div class="modal-footer">
-						<button type="button" class="btn btn-default" id="popUpModalCloseButton">
-							<?xjs write(locale.strings.main.button_close); ?>
-						</button>
-						<button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>
-							<?xjs write(locale.strings.main.button_submit); ?>
-						</button>
-					</div>
-				</div>
-			</div>
-		</div>
+		<?xjs 
 
-		<?xjs if (file_exists(settings.web_lib + '../components/header.xjs')) {
 			(function () {
-				load(xjs_compile(settings.web_lib + '../components/header.xjs'));
+				load(xjs_compile(settings.web_components + 'modal.xjs'));
 			})();
-		} ?>
+
+			if (file_exists(settings.web_lib + '../components/header.xjs')) {
+				(function () {
+					load(xjs_compile(settings.web_lib + '../components/header.xjs'));
+				})();
+			}
+		
+		?>
 
 		<nav class="navbar navbar-default navbar-fixed-top">
 			<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
-- 
GitLab


From 976293c8059c998a767a3b664d604c77825f1372 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:45:20 -0400
Subject: [PATCH 537/752] Move the navbar into its own component file.

---
 web/components/.examples/navbar.xjs | 73 +++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)
 create mode 100644 web/components/.examples/navbar.xjs

diff --git a/web/components/.examples/navbar.xjs b/web/components/.examples/navbar.xjs
new file mode 100644
index 0000000000..23c9eec77f
--- /dev/null
+++ b/web/components/.examples/navbar.xjs
@@ -0,0 +1,73 @@
+<nav class="navbar navbar-default navbar-fixed-top">
+    <div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
+        <div class="navbar-header">
+            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+                <span class="sr-only">
+                    <?xjs write(locale.strings.main.label_sidebar); ?>
+                </span>
+                <span class="icon-bar"></span>
+                <span class="icon-bar"></span>
+                <span class="icon-bar"></span>
+            </button>
+            <a class="navbar-brand" href="./">
+                <?xjs write(settings.brand || system.name); ?>
+            </a>
+        </div>
+        <div id="navbar" class="collapse navbar-collapse">
+            <ul class="nav navbar-nav">
+                <?xjs _menu(getPageList(settings.web_pages)); ?>
+            </ul>
+            <ul class="nav navbar-nav navbar-right">
+                <?xjs if (user.alias === settings.guest || user.number < 1) { ?>
+                    <?xjs if (settings.user_registration) { ?>
+                        <li>
+                            <a href="./?page=000-register.xjs">
+                                <?xjs write(locale.strings.main.menu_item_register); ?>
+                            </a>
+                        </li>
+                    <?xjs } ?>
+                    <li class="nav-item dropdown">
+                        <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+                            <?xjs write(locale.strings.main.menu_item_login); ?>
+                            <span class="caret"></span>
+                        </a>
+                        <div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
+                            <form id="form-login">
+                                <label for="input-username" class="sr-only">
+                                    <?xjs write(locale.strings.main.input_username); ?>
+                                </label>
+                                <input id="input-username" title="<?xjs write(locale.strings.main.input_username); ?>" type="text" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_username); ?>">
+                                <label for="input-password" class="sr-only">
+                                    <?xjs write(locale.strings.main.input_password); ?>
+                                </label>
+                                <input id="input-password" title="<?xjs write(locale.strings.main.input_password); ?>" type="password" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_password); ?>">
+                                <input id="button-login" class="dropdown btn btn-primary" type="submit" value="<?xjs write(locale.strings.main.button_login); ?>">
+                            </form>
+                        </div>
+                    </li>
+                <?xjs } else { ?>
+                    <li class="dropdown">
+                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+                            <?xjs write(user.alias); ?>
+                            <span class="badge scanned" title="Unread mail" id="badge-unread-mail"></span>
+                            <span class="caret"></span>
+                        </a>
+                        <ul class="dropdown-menu">
+                            <li>
+                                <a href="./?page=000-mail.xjs">
+                                    <?xjs write(locale.strings.main.menu_item_mail); ?>
+                                    <span class="badge scanned" title="<?xjs write(locale.strings.main.label_unread_mail); ?>" id="badge-unread-mail-inner"></span>
+                                </a>
+                            </li>
+                            <li>
+                                <a id="button-logout" href="#">
+                                    <?xjs write(locale.strings.main.menu_item_logout); ?>
+                                </a>
+                            </li>
+                        </ul>
+                    </li>
+                <?xjs } ?>
+            </ul>
+        </div>
+    </div>
+</nav>
\ No newline at end of file
-- 
GitLab


From 6284fb6b5aeeb5780638488e536d674bdee79f77 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:45:47 -0400
Subject: [PATCH 538/752] Load navbar component.  Tidy up component paths.

---
 web/root/index.xjs | 88 +++++-----------------------------------------
 1 file changed, 9 insertions(+), 79 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index fdc03bd281..313db93d4e 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -89,87 +89,17 @@
 				load(xjs_compile(settings.web_components + 'modal.xjs'));
 			})();
 
-			if (file_exists(settings.web_lib + '../components/header.xjs')) {
+			if (file_exists(settings.web_components + 'header.xjs')) {
 				(function () {
-					load(xjs_compile(settings.web_lib + '../components/header.xjs'));
+					load(xjs_compile(settings.web_components + 'header.xjs'));
 				})();
 			}
-		
-		?>
 
-		<nav class="navbar navbar-default navbar-fixed-top">
-			<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
-				<div class="navbar-header">
-					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
-						<span class="sr-only">
-							<?xjs write(locale.strings.main.label_sidebar); ?>
-						</span>
-						<span class="icon-bar"></span>
-						<span class="icon-bar"></span>
-						<span class="icon-bar"></span>
-					</button>
-					<a class="navbar-brand" href="./">
-						<?xjs write(settings.brand || system.name); ?>
-					</a>
-				</div>
-				<div id="navbar" class="collapse navbar-collapse">
-					<ul class="nav navbar-nav">
-						<?xjs _menu(getPageList(settings.web_pages)); ?>
-					</ul>
-					<ul class="nav navbar-nav navbar-right">
-						<?xjs if (user.alias === settings.guest || user.number < 1) { ?>
-							<?xjs if (settings.user_registration) { ?>
-								<li>
-									<a href="./?page=000-register.xjs">
-										<?xjs write(locale.strings.main.menu_item_register); ?>
-									</a>
-								</li>
-							<?xjs } ?>
-							<li class="nav-item dropdown">
-								<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-									<?xjs write(locale.strings.main.menu_item_login); ?>
-									<span class="caret"></span>
-								</a>
-								<div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
-									<form id="form-login">
-										<label for="input-username" class="sr-only">
-											<?xjs write(locale.strings.main.input_username); ?>
-										</label>
-										<input id="input-username" title="<?xjs write(locale.strings.main.input_username); ?>" type="text" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_username); ?>">
-										<label for="input-password" class="sr-only">
-											<?xjs write(locale.strings.main.input_password); ?>
-										</label>
-										<input id="input-password" title="<?xjs write(locale.strings.main.input_password); ?>" type="password" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_password); ?>">
-										<input id="button-login" class="dropdown btn btn-primary" type="submit" value="<?xjs write(locale.strings.main.button_login); ?>">
-									</form>
-								</div>
-							</li>
-						<?xjs } else { ?>
-							<li class="dropdown">
-								<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-									<?xjs write(user.alias); ?>
-									<span class="badge scanned" title="Unread mail" id="badge-unread-mail"></span>
-									<span class="caret"></span>
-								</a>
-								<ul class="dropdown-menu">
-									<li>
-										<a href="./?page=000-mail.xjs">
-											<?xjs write(locale.strings.main.menu_item_mail); ?>
-											<span class="badge scanned" title="<?xjs write(locale.strings.main.label_unread_mail); ?>" id="badge-unread-mail-inner"></span>
-										</a>
-									</li>
-									<li>
-										<a id="button-logout" href="#">
-											<?xjs write(locale.strings.main.menu_item_logout); ?>
-										</a>
-									</li>
-								</ul>
-							</li>
-						<?xjs } ?>
-					</ul>
-				</div>
-			</div>
-		</nav>
+			(function () {
+				load(xjs_compile(settings.web_components + 'navbar.xjs'));
+			})();
+
+		?>
 
 		<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
 			<div class="row row-offcanvas row-offcanvas-<?xjs write(settings.layout_sidebar_left ? 'left' : 'right'); ?>">
@@ -186,8 +116,8 @@
 			</div>
 		  	<hr>
 			<footer>
-				<?xjs if (file_exists(settings.web_lib + '../components/footer.xjs')) {
-					load(xjs_compile(settings.web_lib + '../components/footer.xjs'));
+				<?xjs if (file_exists(settings.web_components + 'footer.xjs')) {
+					load(xjs_compile(settings.web_components + 'footer.xjs'));
 				} else { ?>
 					<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
 				<?xjs } ?>
-- 
GitLab


From a3e912e9ebfb6b3f5170c6db8e381725291b0f69 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:46:04 -0400
Subject: [PATCH 539/752] Initialize settings.web_components

---
 web/lib/init.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/init.js b/web/lib/init.js
index 9190c933cc..49dc6e3c8b 100644
--- a/web/lib/init.js
+++ b/web/lib/init.js
@@ -16,7 +16,7 @@ settings.web_root = fullpath(
 	)
 );
 settings.web_lib = backslash(settings.web_directory + 'lib/');
-
+settings.web_components = backslash(settings.web_directory + 'components/');
 settings.web_pages = backslash(fullpath(settings.web_root + '../pages'));
 settings.web_sidebar = backslash(fullpath(settings.web_root + '../sidebar'));
 
-- 
GitLab


From 019deb30240acb9f16592e19a1858c6a1f94f54f Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 12:51:57 -0400
Subject: [PATCH 540/752] Move navbar related functions into navbar component
 file.

---
 web/components/.examples/navbar.xjs | 34 ++++++++++++++++++++++++++++
 web/root/index.xjs                  | 35 ++---------------------------
 2 files changed, 36 insertions(+), 33 deletions(-)

diff --git a/web/components/.examples/navbar.xjs b/web/components/.examples/navbar.xjs
index 23c9eec77f..b2bbdd12d5 100644
--- a/web/components/.examples/navbar.xjs
+++ b/web/components/.examples/navbar.xjs
@@ -1,3 +1,37 @@
+<?xjs function _subMenu(obj, title, path) { ?>
+	<li class="dropdown<?xjs if (path.match(/\//g).length > 1) write('-submenu'); ?>">
+		<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+			<?xjs write(title.replace(/^.*\-/, '')); ?>
+			<span class="caret"></span>
+		</a>
+		<ul class="dropdown-menu">
+			<?xjs _menu(obj.list, path); ?>
+		</ul>
+	</li>
+<?xjs } ?>
+
+<?xjs
+	function _menu(obj, path) {
+		Object.keys(obj).forEach(function (e) {
+			if (obj[e].type == 'list') {
+				_subMenu(obj[e], e, (path || '') + e + '/');
+			} else {
+?>
+                <li>
+                <?xjs if (obj[e].type == 'link') { ?>
+                    <a class="dropdown-item" href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
+                <?xjs } else { ?>
+                    <a class="dropdown-item" href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
+                <?xjs } ?>
+                <? write(e); ?>
+                    </a>
+                </li>
+        <?xjs
+            }
+        });
+    }
+?>
+
 <nav class="navbar navbar-default navbar-fixed-top">
     <div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
         <div class="navbar-header">
diff --git a/web/root/index.xjs b/web/root/index.xjs
index 313db93d4e..4e3f3b3f4a 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -19,42 +19,11 @@
 	var page_ctrl = getCtrlLine(settings.web_pages + page);
 ?>
 
-<?xjs function _subMenu(obj, title, path) { ?>
-	<li class="dropdown<?xjs if (path.match(/\//g).length > 1) write('-submenu'); ?>">
-		<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-			<?xjs write(title.replace(/^.*\-/, '')); ?>
-			<span class="caret"></span>
-		</a>
-		<ul class="dropdown-menu">
-			<?xjs _menu(obj.list, path); ?>
-		</ul>
-	</li>
-<?xjs } ?>
-
 <?xjs
-	function _menu(obj, path) {
-		Object.keys(obj).forEach(function (e) {
-			if (obj[e].type == 'list') {
-				_subMenu(obj[e], e, (path || '') + e + '/');
-			} else {
-?>
-    		<li>
-<?xjs if (obj[e].type == 'link') { ?>
-                <a class="dropdown-item" href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
-<?xjs } else { ?>
-        		<a class="dropdown-item" href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
-<?xjs } ?>
-                    <?xjs write(e); ?>
-                </a>
-    		</li>
-<?xjs }});} ?>
-
-<?xjs function _sidebar() {
+	function _sidebar() {
 		if (settings.layout_sidebar_off || page_ctrl.options.no_sidebar) return;
 ?>
-	<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
-		<?xjs writeSidebarModules(); ?>
-	</div>
+		<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar"><? writeSidebarModules(); ?></div>
 <?xjs } ?>
 
 <!DOCTYPE html>
-- 
GitLab


From 1600535c8cfde71f9e0024a02a257e07f21b5c72 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 13:04:32 -0400
Subject: [PATCH 541/752] Footer closure.

---
 web/root/index.xjs | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/web/root/index.xjs b/web/root/index.xjs
index 4e3f3b3f4a..efcf5e5154 100644
--- a/web/root/index.xjs
+++ b/web/root/index.xjs
@@ -24,7 +24,9 @@
 		if (settings.layout_sidebar_off || page_ctrl.options.no_sidebar) return;
 ?>
 		<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar"><? writeSidebarModules(); ?></div>
-<?xjs } ?>
+<?xjs
+	}
+?>
 
 <!DOCTYPE html>
 <html lang="en">
@@ -33,11 +35,7 @@
 		<meta http-equiv="X-UA-Compatible" content="IE=edge">
 		<meta name="viewport" content="width=device-width, initial-scale=1">
 		<link rel="icon" href="./images/favicon.ico">
-		<title>
-			<?xjs write(page_ctrl.title); ?>
-			:
-			<?xjs write(system.name); ?>
-		</title>
+		<title><? write(page_ctrl.title + ': ' + system.name); ?></title>
 		<link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
 		<link href="./css/offcanvas.css" rel="stylesheet">
 		<link href="./css/style.css" rel="stylesheet">
@@ -52,8 +50,7 @@
 		<script src="./bootstrap/js/bootstrap.min.js"></script>
 		<script src="./js/common.js"></script>
 
-		<?xjs 
-
+		<?xjs
 			(function () {
 				load(xjs_compile(settings.web_components + 'modal.xjs'));
 			})();
@@ -67,7 +64,6 @@
 			(function () {
 				load(xjs_compile(settings.web_components + 'navbar.xjs'));
 			})();
-
 		?>
 
 		<div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
@@ -85,11 +81,17 @@
 			</div>
 		  	<hr>
 			<footer>
-				<?xjs if (file_exists(settings.web_components + 'footer.xjs')) {
-					load(xjs_compile(settings.web_components + 'footer.xjs'));
-				} else { ?>
-					<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
-				<?xjs } ?>
+				<?xjs
+					if (file_exists(settings.web_components + 'footer.xjs')) {
+						(function () {
+							load(xjs_compile(settings.web_components + 'footer.xjs'));
+						})();
+					} else {
+				?>
+						<p>&copy; <?xjs write(system.name + ", " + strftime("%Y")); ?></p>
+				<?xjs
+					}
+				?>
 			</footer>
 		</div>
 
-- 
GitLab


From e36b0b8ce6b6549837571b7dcbf6c683553567a1 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 13:27:12 -0400
Subject: [PATCH 542/752] Format and names of things.

---
 web/components/.examples/navbar.xjs | 64 ++++++++++++-----------------
 1 file changed, 26 insertions(+), 38 deletions(-)

diff --git a/web/components/.examples/navbar.xjs b/web/components/.examples/navbar.xjs
index b2bbdd12d5..9729c50428 100644
--- a/web/components/.examples/navbar.xjs
+++ b/web/components/.examples/navbar.xjs
@@ -1,32 +1,32 @@
-<?xjs function _subMenu(obj, title, path) { ?>
+<?xjs function subMenu(obj, title, path) { ?>
 	<li class="dropdown<?xjs if (path.match(/\//g).length > 1) write('-submenu'); ?>">
 		<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-			<?xjs write(title.replace(/^.*\-/, '')); ?>
+			<? write(title.replace(/^.*\-/, '')); ?>
 			<span class="caret"></span>
 		</a>
 		<ul class="dropdown-menu">
-			<?xjs _menu(obj.list, path); ?>
+			<?xjs menu(obj.list, path); ?>
 		</ul>
 	</li>
 <?xjs } ?>
 
 <?xjs
-	function _menu(obj, path) {
+	function menu(obj, path) {
 		Object.keys(obj).forEach(function (e) {
 			if (obj[e].type == 'list') {
-				_subMenu(obj[e], e, (path || '') + e + '/');
+				subMenu(obj[e], e, (path || '') + e + '/');
 			} else {
 ?>
                 <li>
-                <?xjs if (obj[e].type == 'link') { ?>
-                    <a class="dropdown-item" href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
-                <?xjs } else { ?>
-                    <a class="dropdown-item" href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
-                <?xjs } ?>
-                <? write(e); ?>
-                    </a>
+                    <?xjs if (obj[e].type == 'link') { ?>
+                        <a class="dropdown-item" href="./?page=<?xjs write(obj[e].page); ?>" target="_blank">
+                    <?xjs } else { ?>
+                        <a class="dropdown-item" href="./?page=<?xjs write((path || '') + obj[e].page); ?>">
+                    <?xjs } ?>
+                            <? write(e); ?>
+                        </a>
                 </li>
-        <?xjs
+<?xjs
             }
         });
     }
@@ -36,67 +36,55 @@
     <div class="container<?xjs if (settings.layout_full_width) write('-fluid'); ?>">
         <div class="navbar-header">
             <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
-                <span class="sr-only">
-                    <?xjs write(locale.strings.main.label_sidebar); ?>
-                </span>
+                <span class="sr-only"><? write(locale.strings.main.label_sidebar); ?></span>
                 <span class="icon-bar"></span>
                 <span class="icon-bar"></span>
                 <span class="icon-bar"></span>
             </button>
-            <a class="navbar-brand" href="./">
-                <?xjs write(settings.brand || system.name); ?>
-            </a>
+            <a class="navbar-brand" href="./"><? write(settings.brand || system.name); ?></a>
         </div>
         <div id="navbar" class="collapse navbar-collapse">
             <ul class="nav navbar-nav">
-                <?xjs _menu(getPageList(settings.web_pages)); ?>
+                <?xjs menu(getPageList(settings.web_pages)); ?>
             </ul>
             <ul class="nav navbar-nav navbar-right">
                 <?xjs if (user.alias === settings.guest || user.number < 1) { ?>
                     <?xjs if (settings.user_registration) { ?>
                         <li>
-                            <a href="./?page=000-register.xjs">
-                                <?xjs write(locale.strings.main.menu_item_register); ?>
-                            </a>
+                            <a href="./?page=000-register.xjs"><? write(locale.strings.main.menu_item_register); ?></a>
                         </li>
                     <?xjs } ?>
                     <li class="nav-item dropdown">
                         <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-                            <?xjs write(locale.strings.main.menu_item_login); ?>
+                            <? write(locale.strings.main.menu_item_login); ?>
                             <span class="caret"></span>
                         </a>
                         <div id="login-form" class="dropdown-menu" style="padding:15px; padding-bottom:0px;">
                             <form id="form-login">
-                                <label for="input-username" class="sr-only">
-                                    <?xjs write(locale.strings.main.input_username); ?>
-                                </label>
-                                <input id="input-username" title="<?xjs write(locale.strings.main.input_username); ?>" type="text" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_username); ?>">
-                                <label for="input-password" class="sr-only">
-                                    <?xjs write(locale.strings.main.input_password); ?>
-                                </label>
-                                <input id="input-password" title="<?xjs write(locale.strings.main.input_password); ?>" type="password" class="dropdown form-control" placeholder="<?xjs write(locale.strings.main.input_password); ?>">
-                                <input id="button-login" class="dropdown btn btn-primary" type="submit" value="<?xjs write(locale.strings.main.button_login); ?>">
+                                <label for="input-username" class="sr-only"><? write(locale.strings.main.input_username); ?></label>
+                                <input id="input-username" title="<? write(locale.strings.main.input_username); ?>" type="text" class="dropdown form-control" placeholder="<? write(locale.strings.main.input_username); ?>">
+                                <label for="input-password" class="sr-only"><? write(locale.strings.main.input_password); ?></label>
+                                <input id="input-password" title="<? write(locale.strings.main.input_password); ?>" type="password" class="dropdown form-control" placeholder="<? write(locale.strings.main.input_password); ?>">
+                                <input id="button-login" class="dropdown btn btn-primary" type="submit" value="<? write(locale.strings.main.button_login); ?>">
                             </form>
                         </div>
                     </li>
                 <?xjs } else { ?>
                     <li class="dropdown">
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-                            <?xjs write(user.alias); ?>
+                            <? write(user.alias); ?>
                             <span class="badge scanned" title="Unread mail" id="badge-unread-mail"></span>
                             <span class="caret"></span>
                         </a>
                         <ul class="dropdown-menu">
                             <li>
                                 <a href="./?page=000-mail.xjs">
-                                    <?xjs write(locale.strings.main.menu_item_mail); ?>
+                                    <? write(locale.strings.main.menu_item_mail); ?>
                                     <span class="badge scanned" title="<?xjs write(locale.strings.main.label_unread_mail); ?>" id="badge-unread-mail-inner"></span>
                                 </a>
                             </li>
                             <li>
-                                <a id="button-logout" href="#">
-                                    <?xjs write(locale.strings.main.menu_item_logout); ?>
-                                </a>
+                                <a id="button-logout" href="#"><? write(locale.strings.main.menu_item_logout); ?></a>
                             </li>
                         </ul>
                     </li>
-- 
GitLab


From 8bda466b688c3ab11aee5ea2c629f70a3faa09ce Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 13:30:06 -0400
Subject: [PATCH 543/752] Format.

---
 web/components/.examples/modal.xjs | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/web/components/.examples/modal.xjs b/web/components/.examples/modal.xjs
index 8651ec483a..a7bd3e5375 100644
--- a/web/components/.examples/modal.xjs
+++ b/web/components/.examples/modal.xjs
@@ -2,18 +2,12 @@
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h4 class="modal-title" id="popUpModalTitle">
-                    Pop-Up Thingie
-                </h4>
+                <h4 class="modal-title" id="popUpModalTitle">Pop-Up Thingie</h4>
             </div>
             <div class="modal-body" id="popUpModalBody"></div>
             <div class="modal-footer">
-                <button type="button" class="btn btn-default" id="popUpModalCloseButton">
-                    <?xjs write(locale.strings.main.button_close); ?>
-                </button>
-                <button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden>
-                    <?xjs write(locale.strings.main.button_submit); ?>
-                </button>
+                <button type="button" class="btn btn-default" id="popUpModalCloseButton"><? write(locale.strings.main.button_close); ?></button>
+                <button type="button" class="btn btn-primary" id="popUpModalActionButton" hidden><? write(locale.strings.main.button_submit); ?></button>
             </div>
         </div>
     </div>
-- 
GitLab


From 37f88ed73175ad57873f0c77953f5d90df7718da Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 17:06:57 -0400
Subject: [PATCH 544/752] Make sure that login() is called, and just use its
 return value instead of any other monkey business (this is simpler and
 safer). We'll also increment the user's login counter on success, for now.

---
 web/lib/auth.js | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 7f856f893d..e82441de1f 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -141,10 +141,7 @@ function authenticate(alias, password) {
 	if (un < 1) return false;
 	var usr = new User(un);
 	if (usr.settings&USER_DELETED) return false;
-	if (usr.security.password.toUpperCase() !== password.toUpperCase()) {
-		return false;
-	}
-	login(usr.alias, usr.security.password);
+	if (!login(usr.alias, password, /* inc_logons: */ true)) return false;
 	return usr;
 }
 
-- 
GitLab


From c23b69bd4571e620c20601e059c9e003664a8242 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 23 Aug 2019 17:14:57 -0400
Subject: [PATCH 545/752] Only increment logons if this is a login request and
 not a validation.

---
 web/lib/auth.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index e82441de1f..47fc22b34e 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -71,7 +71,7 @@ function validateSession(cookies) {
 			continue;
 		}
 
-		var _usr = authenticate(usr.alias, usr.security.password);
+		var _usr = authenticate(usr.alias, usr.security.password, false);
 		_usr = undefined;
 		setCookie(usr, session.key);
 		setSessionValue(usr.number, 'ip_address', client.ip_address);
@@ -136,12 +136,12 @@ function destroySession(cookies) {
 
 }
 
-function authenticate(alias, password) {
+function authenticate(alias, password, inc_logons) {
 	var un = system.matchuser(alias);
 	if (un < 1) return false;
 	var usr = new User(un);
 	if (usr.settings&USER_DELETED) return false;
-	if (!login(usr.alias, password, /* inc_logons: */ true)) return false;
+	if (!login(usr.alias, password, inc_logons)) return false;
 	return usr;
 }
 
@@ -158,7 +158,8 @@ function is_user() {
     ) {
     	var usr = authenticate(
     		http_request.query.username[0],
-    		http_request.query.password[0]
+			http_request.query.password[0],
+			true
     	);
 		if (usr instanceof User) {
 			destroySession(http_request.cookie.synchronet || {});
-- 
GitLab


From 65e1b9d79c6c3e45a82d9cccffbf4ff328596b9e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 23 Aug 2019 21:48:32 -0700
Subject: [PATCH 546/752] Specify that we're using UTF-8 in the message body
 text and header fields using the FTN charset method (since we're not creating
 MIME-encoded messages here). Convert .sig files to straight-ASCII, for now.
 We really need a CP437->UTF-8 conversion routine, don't have one yet (in JS).
 Also check the MsgBase.open() return value.

---
 web/lib/forum.js | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index b23f8f21f5..67b85eed1b 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -410,7 +410,7 @@ function getSignature() {
     if (!file_exists(fn)) return '';
     var f = new File(fn);
     f.open('r');
-    var signature = f.read();
+    var signature = ascii_str(f.read());
     f.close();
     return signature;
 }
@@ -438,9 +438,11 @@ function postMessage(sub, header, body) {
         }
         body = lfexpand(body);
         var msgBase = new MsgBase(sub);
-        msgBase.open();
-        ret = msgBase.save_msg(header, word_wrap(body));
-        msgBase.close();
+        if(msgBase.open()) {
+			header.ftn_charset = "UTF-8 4";
+			ret = msgBase.save_msg(header, word_wrap(body));
+			msgBase.close();
+		}
     } catch (err) {
         log(err);
     }
@@ -473,6 +475,7 @@ function postMail(header, body) {
     }
     var msgBase = new MsgBase('mail');
     if (msgBase.open()) {
+		header.ftn_charset = "UTF-8 4";
         ret = msgBase.save_msg(header, lfexpand(body));
         msgBase.close();
     }
-- 
GitLab


From 7fb698135f45080af8605e96a2465cfbe90fa2d3 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 26 Aug 2019 02:01:36 -0700
Subject: [PATCH 547/752] UTF-8 encode user signatures and mark msg headers as
 UTF-8 encoded

When run with a build that includes utf8_encode() (new global JS
function), use it to convert the user's .sig file from CP437 to UTF-8.

When creating a new message (post or email), set the auxattr flag
MSG_HFIELDS_UTF8 since we support UTF-8 chars (not CP437) in message
header fields (e.g. to/from/subject). Requires updated load/smbdefs.js
---
 web/lib/forum.js | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 67b85eed1b..2a35a43e9d 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -410,7 +410,10 @@ function getSignature() {
     if (!file_exists(fn)) return '';
     var f = new File(fn);
     f.open('r');
-    var signature = ascii_str(f.read());
+    if(js.global.utf8_encode)
+	var signature = utf8_encode(f.read());
+    else
+        var signature = ascii_str(f.read());
     f.close();
     return signature;
 }
@@ -440,6 +443,7 @@ function postMessage(sub, header, body) {
         var msgBase = new MsgBase(sub);
         if(msgBase.open()) {
 			header.ftn_charset = "UTF-8 4";
+			header.auxattr = MSG_HFIELDS_UTF8;
 			ret = msgBase.save_msg(header, word_wrap(body));
 			msgBase.close();
 		}
@@ -476,6 +480,7 @@ function postMail(header, body) {
     var msgBase = new MsgBase('mail');
     if (msgBase.open()) {
 		header.ftn_charset = "UTF-8 4";
+		header.auxattr = MSG_HFIELDS_UTF8;
         ret = msgBase.save_msg(header, lfexpand(body));
         msgBase.close();
     }
-- 
GitLab


From eb86d7794a04165242e629d5efc13bca3eaed47e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 26 Aug 2019 17:23:10 -0700
Subject: [PATCH 548/752] Only add the sysop-logons to the logon list if
 configured to do so

There's a setting in SCFG->System->Toggle Options:
"Include Sysop Activity in System Statistics"

This option prevents sysop logons from being "counted" for stats
purposes as well as from being added to the logon list. Make this
so for web logons too.
---
 web/lib/auth.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/web/lib/auth.js b/web/lib/auth.js
index 47fc22b34e..35abf9a9e1 100644
--- a/web/lib/auth.js
+++ b/web/lib/auth.js
@@ -77,8 +77,10 @@ function validateSession(cookies) {
 		setSessionValue(usr.number, 'ip_address', client.ip_address);
 		if (session.session_start === undefined || time() - parseInt(session.session_start, 10) > settings.timeout) {
 			setSessionValue(usr.number, 'session_start', time());
-			const logonlist_lib = load({}, 'logonlist_lib.js');
-			logonlist_lib.add({ node: 'Web' });
+			if(!usr.is_sysop || (system.settings&SYS_SYSSTAT)) {
+				const logonlist_lib = load({}, 'logonlist_lib.js');
+				logonlist_lib.add({ node: 'Web' });
+			}
 		}
 		break;
 
-- 
GitLab


From eadab0b47b576a0e4fe8766b86dbe813d1284c69 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 29 Aug 2019 23:14:22 -0700
Subject: [PATCH 549/752] Support UTF-8 message body text

When message header.is_utf8 is true, don't do the CP437->HTML entity
encoding dance.
---
 web/pages/.examples/001-forum.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/pages/.examples/001-forum.ssjs b/web/pages/.examples/001-forum.ssjs
index 06868d409d..d43118cd60 100644
--- a/web/pages/.examples/001-forum.ssjs
+++ b/web/pages/.examples/001-forum.ssjs
@@ -333,7 +333,7 @@ if (typeof http_request.query.sub !== 'undefined' &&
 
 		// This is a normal message
 		} else {
-			writeln(formatMessage(body, false, settings.forum_extended_ascii));
+			writeln(formatMessage(body, false, settings.forum_extended_ascii && !header.is_utf8));
 			writeln(strings.message.body.close);
 		}
 
-- 
GitLab


From 50785f62de286fdac1b2048d8b00e2813a5bc891 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 4 Sep 2019 18:02:12 -0400
Subject: [PATCH 550/752] Disable web-based user registration by default.

---
 mods/webv4-installer.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index c6630a425b..dd7fa5f5d8 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -143,11 +143,11 @@ if (!modopts_web) {
     	guest : 'Guest',
     	timeout : 43200,
     	inactivity : 900,
-    	user_registration : true,
+    	user_registration : false,
     	minimum_password_length : 6,
     	maximum_telegram_length : 800,
     	web_directory : install_dir,
-      ftelnet : true,
+        ftelnet : true,
     	ftelnet_splash : '../text/synch.ans',
     	keyboard_navigation : false,
     	vote_functions : true,
-- 
GitLab


From 8b1e1fa08912c47a1204150a63a01353c3b2b126 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 4 Sep 2019 23:03:38 -0400
Subject: [PATCH 551/752] Optional new user default settings in
 modopts.ini->[web]

newuser_level
newuser_flags1
newuser_flags2
newuser_flags3
newuser_flags4
newuser_exemptions
newuser_restrictions
---
 web/root/api/register.ssjs | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index 05df075649..ff5215b62d 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -76,6 +76,15 @@ function newUser() {
 		if (property === 'alias' || property === 'password') continue;
 		usr[property] = prepUser[property];
 	}
+	if (typeof settings.newuser_level == 'number' && settings.newuser_level >= 0 && settings.newuser_level <= 99) {
+		usr.security.level = settings.newuser_level;
+	}
+	['flags1', 'flags2', 'flags3', 'flags4', 'exemptions', 'restrictions'].forEach(function (e) {
+		const k = 'newuser_' + e;
+		if (settings[k] && settings[k].search(/[^a-zA-Z]/) < 0) {
+			usr.security[e] = '+' + settings[k];
+		}
+	});
 	reply.userNumber = usr.number;
 }
 
-- 
GitLab


From 791fd33bece20242afa5a8cdd2d97cab6e826273 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 4 Sep 2019 23:12:22 -0400
Subject: [PATCH 552/752] Twiddle users' posts/emails-sent counts as needed.

---
 web/lib/forum.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 2a35a43e9d..7a955ccef3 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -450,6 +450,7 @@ function postMessage(sub, header, body) {
     } catch (err) {
         log(err);
     }
+    if (ret) user.posted_message();
     return ret;
 }
 
@@ -484,6 +485,7 @@ function postMail(header, body) {
         ret = msgBase.save_msg(header, lfexpand(body));
         msgBase.close();
     }
+    if (ret) user.sent_email();
     return ret;
 }
 
@@ -612,6 +614,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
     var ret = msgBase.add_poll(header);
     msgBase.close();
 
+    if (ret) user.posted_message();
     return ret;
 
 }
-- 
GitLab


From 3700d05f2f504a8cc8ec176339bf2b27aaa06524 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Sep 2019 23:25:33 -0400
Subject: [PATCH 553/752] Allow use of existing zip archive

If temp/webv4.zip exists, prompt to overwrite it (default: Yes).
If No is selected, download will be bypassed and the existing
archive will be extracted.
Workaround for those times when downloads from GitHub fail.
---
 mods/webv4-installer.js | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
index dd7fa5f5d8..3196ccdb88 100644
--- a/mods/webv4-installer.js
+++ b/mods/webv4-installer.js
@@ -197,10 +197,12 @@ if (typeof named_parameters.defaults == 'undefined' && deny('Proceed with instal
 }
 write('\r\n\r\n---\r\n\r\n');
 
-writeln('Downloading ' + zip_url);
-if (!download(zip_url, download_target)) {
-    writeln('Download of ' + zip_url + ' failed. Exiting.');
-    exit();
+if (!file_exists(download_target) || confirm(download_target + ' present.  Overwrite')) {
+    writeln('Downloading ' + zip_url);
+    if (!download(zip_url, download_target)) {
+        writeln('Download of ' + zip_url + ' failed. Exiting.');
+        exit();
+    }
 }
 
 writeln('Extracting ' + download_target);
-- 
GitLab


From 340086cc66734f153b7c0c68cae7031753b190a6 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Thu, 5 Sep 2019 23:30:25 -0400
Subject: [PATCH 554/752] Tidying up the registration process

Respect the local "allow dupe realnames" setting.
Slightly more careful checking of some things.
More to come.
---
 web/root/api/register.ssjs | 170 +++++++++++--------------------------
 1 file changed, 50 insertions(+), 120 deletions(-)

diff --git a/web/root/api/register.ssjs b/web/root/api/register.ssjs
index ff5215b62d..0749730ddc 100644
--- a/web/root/api/register.ssjs
+++ b/web/root/api/register.ssjs
@@ -2,6 +2,7 @@ require('sbbsdefs.js', 'SYS_CLOSED');
 var settings = load('modopts.js', 'web');
 load(settings.web_directory + '/lib/init.js');
 load(settings.web_lib + 'auth.js');
+load(settings.web_lib + 'request.js');
 
 if (user.alias !== settings.guest) exit();
 if (!settings.user_registration) exit();
@@ -36,32 +37,23 @@ function required(mask) {
 	return (system.new_user_questions&mask);
 }
 
-function cleanParam(param) {
-	if (paramExists(param)) {
-		return http_request.query[param][0].replace(/[\x00-\x19\x7F]/g, '');
-	}
+function clean_param(param) {
+	if (Request.has_param(param)) return Request.get_param(param).replace(/[\x00-\x19\x7F]/g, '');
 	return "";
 }
 
-function paramExists(param) {
-	if (typeof http_request.query[param] !== 'undefined' &&
-		http_request.query[param][0] !== ''
-	) {
-		return true;
-	}
-	return false;
-}
-
-function paramLength(param) {
-	if (typeof http_request.query[param] === 'undefined') {
-		return 0;
-	} else if (http_request.query[param][0].replace(' ', '').length < 1) {
-		return 0;
-	} else if (cleanParam(param).length < 1) {
-		return 0;
-	} else {
-		return http_request.query[param][0].length;
-	}
+function in_range(n, min, max) {
+	return n >= min && n <= max;
+}
+
+function valid_param(p, min, max) {
+	if (!Request.has_param(p)) return false;
+	if (!in_range(clean_param(p).length, min, max)) return false;
+	return true;
+}
+
+function is_dupe(field, str) {
+	return system.matchuserdata(field, str) !== 0;
 }
 
 function newUser() {
@@ -89,136 +81,74 @@ function newUser() {
 }
 
 // See if the hidden form fields were filled
-if ((	paramExists('send-me-free-stuff') &&
-		http_request.query['send-me-free-stuff'][0] !== ''
-	) ||
-	(	paramExists('subscribe-to-newsletter') &&
-		http_request.query['subscribe-to-newsletter'][0] !== ''
-	)
-) {
+if (Request.get_param('send-me-free-stuff') != '' || Request.get_param('subscribe-to-newsletter') !== undefined) {
 	log(LOG_WARNING, locale.strings.api_register.log_bot_attempt);
 	exit();
 }
 
-if (system.newuser_password !== '' &&
-	(	typeof http_request.query['newuser-password'] === 'undefined' ||
-		http_request.query['newuser-password'][0] != system.newuser_password
-	)
-) {
+if (system.newuser_password !== '' && (!Request.has_param('newuser-password') || Request.get_param('newuser-password') != system.newuser_password)) {
 	reply.errors.push(locale.strings.api_register.error_bad_syspass);
 }
 
-// More could be done to respect certain newuser question toggles
-// (UQ_DUPREAL, UQ_NOUPPRLWR, UQ_NOCOMMAS), but I don't care right now.
-
-if (!paramExists('alias') ||
-	paramLength('alias') < MIN_ALIAS ||
-	paramLength('alias') > LEN_ALIAS ||
-	!system.check_name(http_request.query.alias[0])
-) {
+if (!valid_param('alias', MIN_ALIAS, LEN_ALIAS) || !system.check_name(clean_param('alias'))) {
 	reply.errors.push(locale.strings.api_register.error_invalid_alias);
-} else if (system.matchuser(http_request.query.alias[0]) > 0) {
+} else if (system.matchuser(clean_param('alias')) > 0) {
 	reply.errors.push(locale.strings.api_register.error_alias_taken);
 } else {
-	prepUser.alias = cleanParam('alias');
-	prepUser.handle = cleanParam('alias');
+	prepUser.alias = clean_param('alias');
+	prepUser.handle = clean_param('alias');
 }
 
-if ((!paramExists('password1') || !paramExists('password2')) ||
-	http_request.query.password1[0] !== http_request.query.password2[0]
-) {
+if (!Request.has_param('password1') || !Request.has_param('password2') || clean_param('password1') != clean_param('password2')) {
 	reply.errors.push(locale.strings.api_register.error_password_mismatch);
-} else if (
-	paramLength('password1') < settings.minimum_password_length ||
-	paramLength('password1') > LEN_PASS
-) {
-	reply.errors.push(
-		format(
-			locale.strings.api_register.error_password_length,
-			settings.minimum_password_length, LEN_PASS
-		)
-	);
+} else if (!in_range(clean_param('password1').length, settings.minimum_password_length, LEN_PASS)) {
+	reply.errors.push(format(locale.strings.api_register.error_password_length, settings.minimum_password_length, LEN_PASS));
 } else {
-	prepUser.password = cleanParam('password1');
+	prepUser.password = clean_param('password1');
 }
 
-if (!paramExists('netmail') && !required(UQ_NONETMAIL)) {
+if (valid_param('netmail', MIN_NETMAIL, LEN_NETMAIL)) {
+	prepUser.netmail = clean_param('netmail');
+} else if (!required(UQ_NONETMAIL)) {
 	reply.errors.push(locale.strings.api_register.error_email_required);
-} else if (
-	(	paramLength('netmail') < MIN_NETMAIL ||
-		paramLength('netmail') > LEN_NETMAIL
-	) && !required(UQ_NONETMAIL)
-) {
-	reply.errors.push(locale.strings.api_register.error_invalid_email);
-} else {
-	prepUser.netmail = cleanParam('netmail');
 }
 
-if (required(UQ_REALNAME) &&
-	(	!paramExists('realname') ||
-		paramLength('realname') < MIN_REALNAME ||
-		paramLength('realname') > LEN_NAME ||
-		!system.check_name(http_request.query.alias[0])
-	)
-) {
+if (valid_param('realname', MIN_REALNAME, LEN_NAME) && (!required(UQ_DUPREAL) || !is_dupe(U_NAME, clean_param('realname')))) {
+	prepUser.name = clean_param('realname');
+} else if (required(UQ_REALNAME)) {
 	reply.errors.push(locale.strings.api_register.error_invalid_name);
-} else {
-	prepUser.name = cleanParam('realname');
 }
 
-if (required(UQ_LOCATION) &&
-	(	!paramExists('location') ||
-		paramLength('location') < MIN_LOCATION ||
-		paramLength('location') > LEN_LOCATION
-	)
-) {
+// UQ_NOCOMMAS should be checked and acted on
+if (valid_param('location', MIN_LOCATION, LEN_LOCATION)) {
+	prepUser.location = clean_param('location');
+} else if (required(UQ_LOCATION)) {
 	reply.errors.push(locale.strings.api_register.error_invalid_location);
-} else {
-	prepUser.location = cleanParam('location');
-}
-
-if (required(UQ_ADDRESS) &&
-	(	!paramExists('address') ||
-		paramLength('address') < MIN_ADDRESS ||
-		paramLength('address') > LEN_ADDRESS ||
-		!paramExists('zipcode') ||
-		paramLength('zipcode') < 3 ||
-		paramLength('zipcode') > LEN_ADDRESS
-	)
-) {
+}
+
+if (valid_param('address', MIN_ADDRESS, LEN_ADDRESS) && valid_param('zipcode', 3, LEN_ADDRESS)) {
+	prepUser.address = clean_param('address');
+	prepUser.zipcode = clean_param('zipcode');
+} else if (required(UQ_ADDRESS)) {
 	reply.errors.push(locale.strings.api_register.error_invalid_street_address);
-} else {
-	prepUser.address = cleanParam('address');
-	prepUser.zipcode = cleanParam('zipcode');
 }
 
-if (required(UQ_PHONE) &&
-	(	!paramExists('phone') ||
-		paramLength('phone') < MIN_PHONE ||
-		paramLength('phone') > LEN_PHONE
-	)
-) {
+// Validate?  Who cares?
+if (valid_param('phone', MIN_PHONE, LEN_PHONE)) {
+	prepUser.phone = clean_param('phone');
+} else if (required(UQ_PHONE)) {
 	reply.errors.push(locale.strings.api_register.error_invalid_phone);
-} else {
-	prepUser.phone = cleanParam('phone');
 }
 
-if (required(UQ_SEX) &&
-	(	!paramExists('gender') ||
-		paramLength('gender') != 1 ||
-		['X','M','F','O'].indexOf(http_request.query.gender[0]) < 0
-	)
-) {
+if (valid_param('gender', 1, 1) && ['X', 'M', 'F', 'O'].indexOf(Request.get_param('gender')) > -1) {
+	prepUser.birthdate = clean_param('gender');
+} else if (required(UQ_SEX)) {
 	reply.errors.push(locale.strings.api_register.error_invalid_gender);
-} else {
-	prepUser.gender = http_request.query.gender[0];
 }
 
-if (paramExists('birth') &&
-	http_request.query.birth[0].match(/^\d\d\/\d\d\/\d\d$/) !== null
-) {
+if (Request.has_param('birth') && clean_param('birth').match(/^\d\d\/\d\d\/\d\d$/) !== null) {
 	// Should really test for valid date (and date format per system config)
-	prepUser.birthdate = cleanParam('birth');
+	prepUser.birthdate = clean_param('birth');
 } else if (required(UQ_BIRTH)) {
 	reply.errors.push(locale.strings.api_register.error_invalid_birthdate);
 }
-- 
GitLab


From 2a416b10ba8b2fe76f555059a995bb30be96a077 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 15 Sep 2019 18:05:43 -0700
Subject: [PATCH 555/752] utf8_decode() telegrams (to ASCII/CP437)

We don't (yet) support UTF-8 sequences in telegrams (short messages to users).
---
 web/root/api/system.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/root/api/system.ssjs b/web/root/api/system.ssjs
index 8efd10fec5..1ed5bc1005 100644
--- a/web/root/api/system.ssjs
+++ b/web/root/api/system.ssjs
@@ -65,7 +65,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				un, format(
 					locale.strings.api_system.telegram_header_format,
 					user.alias, (new Date()).toLocaleString()
-				) + '\r\n' + http_request.query.telegram[0] + '\r\n'
+				) + '\r\n' + utf8_decode(http_request.query.telegram[0]) + '\r\n'
 			);
 			break;
 
-- 
GitLab


From 6a703516039083ad882e74165ae98a149cc38d0f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 17 Sep 2019 20:28:11 -0700
Subject: [PATCH 556/752] Convert non-UTF-8 message header fields (to/from) to
 UTF-8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Some messages come from non-English locales with non-UTF-8 charsets
(e.g. CP437), so their to/from fields may contain non-ASCII/non-UTF-8
characters (e.g. "Björn Felte") - so convert these fields (to/from)
to UTF-8, as needed. Uses the new(ish) utf8_encode() function, so you
must have a recent v3.17c build.
I did not transcode message subjects as I'm not clear how they are stored
and copied here. That's probably something better handled by echicken.
---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 7a955ccef3..d2bc67cc7b 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -948,10 +948,10 @@ function getMessageThreads(sub, max) {
             attr : header.attr,
             auxattr : header.auxattr,
             number : header.number,
-            from : header.from,
+            from : header.is_utf8 ? header.from : utf8_encode(header.from),
             from_ext : header.from_ext,
             from_net_addr : header.from_net_addr,
-            to : header.to,
+            to : header.is_utf8 ? header.to : utf8_encode(header.to),
             when_written_time : header.when_written_time,
             upvotes : (header.attr&MSG_POLL ? 0 : (header.upvotes || 0)),
             downvotes : (header.attr&MSG_POLL ? 0 : (header.downvotes || 0)),
-- 
GitLab


From 2fbe160dcc4792a4f641e5079e82a86602969f3f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 24 Sep 2019 16:33:39 -0700
Subject: [PATCH 557/752] It was observed that httpSess threads would consume
 ~15% of a CPU

Using Linux top, long-running (hundreds of hours) httpSess threads were seen
consuming ~15% of a CPU core. This loop calls time() and yield() in a tight
loop, so instead mswait(50) (milliseconds), which reduces the CPU utilization
considerable (down to < 1%), but will slightly impact the frequency with
which the events callbacks are invoked. A better scheme would be to block
while waiting for an event or a timeout (e.g. 1 second), so this is not a
great solution. More of a work-around.
---
 web/root/api/events.ssjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/root/api/events.ssjs b/web/root/api/events.ssjs
index 01135feede..93d50b7191 100644
--- a/web/root/api/events.ssjs
+++ b/web/root/api/events.ssjs
@@ -53,6 +53,6 @@ while (client.socket.is_connected) {
             delete callbacks[e];
         }
     });
-    yield();
+    mswait(50);
     ping();
-}
\ No newline at end of file
+}
-- 
GitLab


From b3e9a28ad8124e53a4b75cc1e30203726a897513 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 5 Oct 2019 13:38:54 -0700
Subject: [PATCH 558/752] Don't allow replying to one's self

When posting a reply-message, if the original "from" field is the
current user's alias, then reply to the original message's "to"
value instead of the "from" value. This is also how sbbs handles
reply 'to' fields.
---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index d2bc67cc7b..47bf5eabf2 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -533,7 +533,7 @@ function postReply(sub, body, pid) {
         msgBase.close();
         if (pHeader === null) return ret;
         var header = {
-            'to' : pHeader.from,
+            'to' : pHeader.from == user.alias ? pHeader.to : pHeader.from,
             'from' : user.alias,
             'subject' : pHeader.subject,
             'thread_id' : (
-- 
GitLab


From 08a2638b8d9b825d7861e36c122225a838d61a42 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Wed, 8 Jan 2020 13:37:52 -0500
Subject: [PATCH 559/752] Break out spam attribute/subject check into a
 function for use in the mail and forum pages.

If modopts -> [web] -> forum_no_spam, then filter spam messages
in the forum.  (Maybe make this more advanced in the future so
users can see these messages if they want to.)
---
 web/lib/forum.js | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 47bf5eabf2..e8a5857be7 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -312,6 +312,10 @@ function getMailHeaders(sent, ascending) {
     return headers;
 }
 
+function is_spam(header) {
+    return (header.attr&MSG_SPAM || (header.subject.search(/^SPAM:/) > -1));
+}
+
 function get_mail_headers(filter, ascending) {
     const ret = {
         headers: [],
@@ -330,7 +334,7 @@ function get_mail_headers(filter, ascending) {
             if (filter == 'sent') ret.headers.push(h);
         }
 	if (h.to_ext == user.number) {
-            if ((h.attr&MSG_SPAM) || (h.subject.search(/^SPAM:/) > -1)) {
+            if (is_spam(h)) {
                 h.attr&MSG_READ ? ret.spam.read++ : (ret.spam.unread++);
                 if (filter == 'spam') ret.headers.push(h);
             } else {
@@ -998,6 +1002,7 @@ function getMessageThreads(sub, max) {
         for (var m = start; m <= msgBase.last_msg; m++) {
             var header = msgBase.get_msg_header(m);
             if (header === null || header.attr&MSG_DELETE) continue;
+            if (settings.forum_no_spam && is_spam(header)) continue;
             headers[header.number] = header;
             c++;
             if (c >= count) break;
@@ -1024,6 +1029,11 @@ function getMessageThreads(sub, max) {
                 return;
             }
 
+            if (settings.forum_no_spam && is_spam(header)) {
+                delete headers[h];
+                return;
+            }
+
             if (sub === 'mail' &&
                 headers[h].to !== user.alias &&
                 headers[h].to !== user.name &&
-- 
GitLab


From ae656cd54b06474a78f1a9129029f3700b46da38 Mon Sep 17 00:00:00 2001
From: Fernando Toledo <ftoledo@docksud.com.ar>
Date: Mon, 10 Feb 2020 00:09:20 -0300
Subject: [PATCH 560/752] begin spanish translation

---
 web/lib/locale/es_ar.ini | 144 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100644 web/lib/locale/es_ar.ini

diff --git a/web/lib/locale/es_ar.ini b/web/lib/locale/es_ar.ini
new file mode 100644
index 0000000000..2cbe5d57ba
--- /dev/null
+++ b/web/lib/locale/es_ar.ini
@@ -0,0 +1,144 @@
+[main]
+button_close = Cerrar
+button_submit = Enviar
+button_login = Iniciar sesión
+input_username = Usuario
+input_password = Clave
+label_sidebar = Sidebar
+label_unread_mail = Ne leídos
+menu_item_login = Ingresar
+menu_item_logout = Salir
+menu_item_mail = Correo
+menu_item_register = Registrar
+
+[page_home]
+button_ftelnet = Conectar via Telnet
+
+[page_mail]
+button_post_new = Escribir un mensaje
+button_select_all = Seleccionar todos
+button_delete_selected = Eliminar seleccionados
+label_message_from = De
+label_message_to = Para
+label_message_date = el
+label_message_subject = Asunto
+label_tab_inbox = Entrada
+label_tab_sent = Enviados
+
+[page_register]
+title = Registrar
+button_register = Registrar
+label_field_required = Requerido
+input_alias = Usuario
+input_password = Clave
+input_password_confirm = Confirmar clave
+input_email = Dirección de Email
+input_name = Nombre real
+input_street_address = Dirección
+input_zipcode = Cod. Postal
+input_location = Lugar (Ciudad, Estado)
+input_phone = Teléfono
+input_birthdate = Nacimiento
+input_gender = Genero
+input_gender_withheld = Retenido
+input_gender_male = Masculino
+input_gender_female = Femenino
+input_gender_other = Otro
+input_registration_password = Clave de registro
+stat_suffix_field_required = obligatorio
+help_text_required = Campos marcados con asterisco son obligatorios.  Todo los demas pueden quedar en blanco si lo desea.
+help_text_minimum_characters = Mínimo de %d caracteres
+help_text_maximum_characters = Máximo de %d caracteres
+message_account_created = Su cuenta ha sido creada
+placeholder_netmail = jp@m.f
+placeholder_name = Juan Perez 
+placeholder_street_address = Calle 123
+placeholder_zipcode = 92860
+placeholder_location = Ciudad, Estado
+placeholder_phone = 800-555-5555
+
+[page_files]
+title = Archivos
+stat_suffix_file = archivo
+stat_suffix_files = archivos
+stat_suffix_directory = directorio
+stat_suffix_directories = directorios
+
+[page_forum]
+title = XJS Foros de prueba
+sub_boards = sub-boards
+label_thread_from = Por
+label_message_from = De
+label_message_to = Para
+label_message_date = el
+label_message_subject = Asunto
+label_thread_latest_reply = última respuesta de
+suffix_reply_count = respuesta
+suffix_replies_count = respuestas
+badge_poll = ENCUESTA
+badge_downvotes = Downvotes - Parent / Thread Total
+badge_upvotes = Upvotes - Parent / Thread Total
+badge_unread_messages = No leidos
+button_post_message = Nuevo mensaje
+button_post_poll = Nueva encuesta
+button_scan_new = Include in new message scan
+button_scan_you = Include in new message scan (messages to you only)
+button_scan_off = Exclude from new message scan
+button_thread_first_page = First page
+button_thread_back_pages = Jump %s page(s) backward
+button_thread_previous_page = Previous page
+button_thread_next_page = Next page
+button_thread_forward_pages = Jump %s page(s) forward
+button_thread_last_page = Last page
+
+[sidebar_node_list]
+label_title = Who's Online
+label_connection_column = Via
+label_node_column = Node
+label_send_telegram = Send a telegram
+label_status_column = Status
+label_status_web = browsing
+
+[sidebar_recent_visitors]
+label_title = Recent Visitors
+label_location = from
+label_connection = via
+
+[sidebar_system_info]
+label_title = System Info
+label_sysop = Sysop:
+label_location = Location:
+label_users = Users:
+label_nodes = Nodes:
+label_uptime = Uptime:
+label_calls_total = Calls:
+label_calls_today = Calls today:
+label_files_total = Files:
+label_files_uploaded_today = U/L today:
+label_files_downloaded_today = D/L today:
+label_messages_total = Messages:
+label_messages_posted_today = Posted today:
+stat_suffix_files = files
+stat_suffix_bytes = bytes
+
+[api_system]
+nodelist_action_prefix = viewing
+telegram_header_format = Telegram from %s via WWW on %s
+
+[api_register]
+log_success = User #%d registered via HTTP
+log_bot_attempt = Hidden registration form field filled. Likely a bot. Cancelling registration.
+error_failed = Failed to create user record.
+error_bad_syspass = Incorrect registration password.
+error_invalid_alias = Valid username is required.
+error_alias_taken = Username already taken.
+error_password_mismatch = Password and confirmation are required, and must match.
+error_password_length = Password must be %d to %d characters in length.
+error_email_required = Valid email address is required.
+error_invalid_email = Invalid email address
+error_invalid_name = Valid real name is required.
+error_invalid_location = Valid location is required.
+error_invalid_street_address = Valid street address and postal code are required
+error_invalid_phone = Valid phone number is required.
+error_invalid_gender = Sex is required. Heh heh heh.
+error_invalid_birthdate = Valid birthdate is required.
-- 
GitLab


From 111831bce424b6bb884f568cb01edba786ffbd78 Mon Sep 17 00:00:00 2001
From: Fernando Toledo <ftoledo@docksud.com.ar>
Date: Mon, 10 Feb 2020 00:21:57 -0300
Subject: [PATCH 561/752] siguiendo traducciones

---
 web/lib/locale/es_ar.ini | 92 ++++++++++++++++++++--------------------
 1 file changed, 46 insertions(+), 46 deletions(-)

diff --git a/web/lib/locale/es_ar.ini b/web/lib/locale/es_ar.ini
index 2cbe5d57ba..c7132ea261 100644
--- a/web/lib/locale/es_ar.ini
+++ b/web/lib/locale/es_ar.ini
@@ -81,64 +81,64 @@ badge_upvotes = Upvotes - Parent / Thread Total
 badge_unread_messages = No leidos
 button_post_message = Nuevo mensaje
 button_post_poll = Nueva encuesta
-button_scan_new = Include in new message scan
-button_scan_you = Include in new message scan (messages to you only)
-button_scan_off = Exclude from new message scan
+button_scan_new = Incluir en escaneo de nuevos mensajes
+button_scan_you = Incluir en escaneo de nuevos mensajes (mensajes solo para ud.)
+button_scan_off = Excluir del escaneo de nuevos mensajes
 button_thread_first_page = First page
-button_thread_back_pages = Jump %s page(s) backward
-button_thread_previous_page = Previous page
-button_thread_next_page = Next page
-button_thread_forward_pages = Jump %s page(s) forward
-button_thread_last_page = Last page
+button_thread_back_pages = Saltar %s página(s) atras
+button_thread_previous_page = Página anterior
+button_thread_next_page = Página siguiente
+button_thread_forward_pages = Salta %s página(s) adelante
+button_thread_last_page = Última página
 
 [sidebar_node_list]
-label_title = Who's Online
-label_connection_column = Via
-label_node_column = Node
-label_send_telegram = Send a telegram
-label_status_column = Status
-label_status_web = browsing
+label_title = Quien está en Línea
+label_connection_column = Vía
+label_node_column = Nodo
+label_send_telegram = Enviar Telegram
+label_status_column = Estado
+label_status_web = navegando
 
 [sidebar_recent_visitors]
-label_title = Recent Visitors
-label_location = from
-label_connection = via
+label_title = Visitas Recientes
+label_location = desde
+label_connection = vía
 
 [sidebar_system_info]
-label_title = System Info
+label_title = Información el Systema
 label_sysop = Sysop:
-label_location = Location:
-label_users = Users:
-label_nodes = Nodes:
+label_location = Lugar:
+label_users = Usuarios:
+label_nodes = Nodos:
 label_uptime = Uptime:
-label_calls_total = Calls:
-label_calls_today = Calls today:
-label_files_total = Files:
-label_files_uploaded_today = U/L today:
-label_files_downloaded_today = D/L today:
-label_messages_total = Messages:
-label_messages_posted_today = Posted today:
-stat_suffix_files = files
+label_calls_total = Llamadas:
+label_calls_today = Llamadas de hoy:
+label_files_total = Archivoss:
+label_files_uploaded_today = U/L hoy:
+label_files_downloaded_today = D/L hoy:
+label_messages_total = Mensajes:
+label_messages_posted_today = Mensajes hoy:
+stat_suffix_files = archivos
 stat_suffix_bytes = bytes
 
 [api_system]
-nodelist_action_prefix = viewing
-telegram_header_format = Telegram from %s via WWW on %s
+nodelist_action_prefix = viendo
+telegram_header_format = Telegram de %s vía WWW el %s
 
 [api_register]
-log_success = User #%d registered via HTTP
+log_success = Usuario #%d registrado vía HTTP
 log_bot_attempt = Hidden registration form field filled. Likely a bot. Cancelling registration.
-error_failed = Failed to create user record.
-error_bad_syspass = Incorrect registration password.
-error_invalid_alias = Valid username is required.
-error_alias_taken = Username already taken.
-error_password_mismatch = Password and confirmation are required, and must match.
-error_password_length = Password must be %d to %d characters in length.
-error_email_required = Valid email address is required.
-error_invalid_email = Invalid email address
-error_invalid_name = Valid real name is required.
-error_invalid_location = Valid location is required.
-error_invalid_street_address = Valid street address and postal code are required
-error_invalid_phone = Valid phone number is required.
-error_invalid_gender = Sex is required. Heh heh heh.
-error_invalid_birthdate = Valid birthdate is required.
+error_failed = Error al crear el registro de usuario.
+error_bad_syspass = Clave incorrecta.
+error_invalid_alias = Usuario es obligatorio.
+error_alias_taken = Usuario ya existente.
+error_password_mismatch = La clave y confirmación son obligatorios y deben concidir.
+error_password_length = La clave debe ser de %d a %d caracteres de longitud.
+error_email_required = Dirección de E-mail es obligatoria.
+error_invalid_email = Direccion de E-mail inválida.
+error_invalid_name = Nombre real es obligatorio.
+error_invalid_location = Lugar es obligatorio.
+error_invalid_street_address = Direccion y Cod. postal son obligatorios.
+error_invalid_phone = Telefono es obligatorio.
+error_invalid_gender = Sexo es obligatorio. Heh heh heh.
+error_invalid_birthdate = Nacimiento es obligatorio.
-- 
GitLab


From 5e55617d150550a4dedd156403b89dc20e6c6642 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 12 Apr 2020 22:07:48 -0700
Subject: [PATCH 562/752] Use User.downloaded_file() to do "all the things"

The User.downloaded_file() method has been enhanced to accept a directory
code and a filename to do "all the things" expected after a file has been
successfully downloaded. Use the method.
---
 web/root/api/files.ssjs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index cfcc48d948..a45a52bc36 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -18,7 +18,8 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				file_area.dir[http_request.query.dir[0]].can_download &&
 				typeof http_request.query.file !== 'undefined'
 			) {
-				var fileBase = new FileBase(file_area.dir[http_request.query.dir[0]].code);
+				var dircode = file_area.dir[http_request.query.dir[0]].code;
+				var fileBase = new FileBase(dircode);
 				var file = null;
 				fileBase.some(function (e) {
 					if (e.base.toLowerCase() + '.' + e.ext.toLowerCase() !== http_request.query.file[0].toLowerCase()) {
@@ -42,6 +43,7 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 				}
 				f.close();
 				reply = false;
+				user.downloaded_file(dircode, file.path);
 			}
 			break;
 		default:
-- 
GitLab


From 6dc37b6dd01ec0bbbb46bc12d284e720a7c0866d Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 13 Apr 2020 01:41:37 -0400
Subject: [PATCH 563/752] Prevent download if !dir is_exempt and file credits >
 user total credits. Set an 'error' property on reply if file not found or not
 enough credits.

---
 web/root/api/files.ssjs | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index a45a52bc36..f128ffbaea 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -1,3 +1,4 @@
+load('sbbsdefs.js');
 var settings = load('modopts.js', 'web');
 
 load(settings.web_directory + '/lib/init.js');
@@ -29,7 +30,14 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 						return true;
 					}
 				});
-				if (file === null) break;
+				if (file === null) {
+					reply.error = 'File not found';
+					break;
+				}
+				if (!file_area.dir[dircode].is_exempt && file.credits > (user.security.credits + user.security.free_credits)) {
+					reply.error = 'Not enough credits to download this file';
+					break;
+				}
 				http_reply.header['Content-Type'] = 'application/octet-stream';
 				http_reply.header['Content-Disposition'] = 'attachment; filename="' + file.base + '.' + file.ext + '"';
 				http_reply.header['Content-Encoding'] = 'binary';
-- 
GitLab


From c45cb59ed0657509b16dfa44c005fa1b2f4bc819 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Tue, 28 Apr 2020 16:23:28 -0400
Subject: [PATCH 564/752] Garbage.

---
 web/sidebar/.extras/openweathermap.ssjs | 23 -----------------------
 1 file changed, 23 deletions(-)
 delete mode 100644 web/sidebar/.extras/openweathermap.ssjs

diff --git a/web/sidebar/.extras/openweathermap.ssjs b/web/sidebar/.extras/openweathermap.ssjs
deleted file mode 100644
index c269e60c6a..0000000000
--- a/web/sidebar/.extras/openweathermap.ssjs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Requires an API key from https://openweathermap.org/
-// Add an [openweathermap] section to ctrl/modopts.ini
-// Add an api_key value to that section
-(function () {
-    try {
-        load('geoip.js');
-        require('openweathermap.js', 'OpenWeatherMap');
-        const geoip = get_geoip(
-            http_request.header['x-forwarded-for'] || http_request.remote_ip
-        );
-        const owm = new OpenWeatherMap();
-        const wq = { units: 'metric', mode: 'html' };
-        if (geoip.latitude && geoip.longitude) {
-            wq.lat = geoip.latitude;
-            wq.lon = geoip.longitude;
-        } else if (geoip.cityName) {
-            wq.q = geoip.cityName;
-        }
-        writeln(owm.call_api('weather', wq, true).data);
-    } catch (err) {
-        // meh
-    }
-})();
-- 
GitLab


From 73716a5405d680c5c153e186007cc44cd90d27fa Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 2 May 2020 21:02:54 -0700
Subject: [PATCH 565/752] Fix the display of anonymous messages and posting on
 anon-only subs

Don't display the name of messages posted with the anonymous attribute
and force messages posted to anonymous-only subs as anonymous.
---
 web/lib/forum.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index e8a5857be7..006cdb573e 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -587,7 +587,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
     var header = {
         attr : MSG_POLL,
         subject : subject.substr(0, LEN_TITLE),
-        from : msg_area.sub[sub].settings&SUB_NAME ? user.name : user.alias,
+        from : msg_area.sub[sub].settings&SUB_AONLY ? "Anonymous" : (msg_area.sub[sub].settings&SUB_NAME ? user.name : user.alias),
         from_ext : user.number,
         to : 'All',
         field_list : [],
@@ -952,7 +952,7 @@ function getMessageThreads(sub, max) {
             attr : header.attr,
             auxattr : header.auxattr,
             number : header.number,
-            from : header.is_utf8 ? header.from : utf8_encode(header.from),
+            from : (header.attr&MSG_ANONYMOUS) ? "Anonymous" : (header.is_utf8 ? header.from : utf8_encode(header.from)),
             from_ext : header.from_ext,
             from_net_addr : header.from_net_addr,
             to : header.is_utf8 ? header.to : utf8_encode(header.to),
-- 
GitLab


From 370247eba462b340735dcf397984dec3b8647efe Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 6 May 2020 16:57:44 -0700
Subject: [PATCH 566/752] Poll subjects (questions) are UTF-8 encoded when
 posted from web

---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index 006cdb573e..ba0595fa1d 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -591,7 +591,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
         from_ext : user.number,
         to : 'All',
         field_list : [],
-        auxattr : (results<<POLL_RESULTS_SHIFT),
+        auxattr : (results<<POLL_RESULTS_SHIFT) | MGS_HFIELDS_UTF8,
         votes : votes
     };
 
-- 
GitLab


From ca5146901092698cf4709dfb2e691513b4603a32 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Mon, 20 Jul 2020 16:11:19 -0400
Subject: [PATCH 567/752] Don't prevent default click thing from doing its
 click thing. (Jump back to #fTelnet anchor when doing the click thing on a
 clickable click thing to do the thing.) Complaint courtesy of Android8675309

---
 web/pages/.examples/003-games.xjs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web/pages/.examples/003-games.xjs b/web/pages/.examples/003-games.xjs
index 41e73bdaf1..9f6ae74237 100644
--- a/web/pages/.examples/003-games.xjs
+++ b/web/pages/.examples/003-games.xjs
@@ -83,7 +83,6 @@
             var a = $(li).find('a')[0];
             $(a).text(xx.n);
             $(a).click(function (evt) {
-                evt.preventDefault();
                 launchXtrn(xx.c);
             });
             $(ul).append(li);
-- 
GitLab


From 1d78115925b6182799322ea15da390bf51df3df3 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 12 Aug 2020 20:45:12 -0700
Subject: [PATCH 568/752] Fix typo in my last commit to this file (from May 6)

Fixes error: ReferenceError: MGS_HFIELDS_UTF8 is not defined
when attempting to post a poll.
---
 web/lib/forum.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/forum.js b/web/lib/forum.js
index ba0595fa1d..9dcf3cca65 100644
--- a/web/lib/forum.js
+++ b/web/lib/forum.js
@@ -591,7 +591,7 @@ function postPoll(sub, subject, votes, results, answers, comments) {
         from_ext : user.number,
         to : 'All',
         field_list : [],
-        auxattr : (results<<POLL_RESULTS_SHIFT) | MGS_HFIELDS_UTF8,
+        auxattr : (results<<POLL_RESULTS_SHIFT) | MSG_HFIELDS_UTF8,
         votes : votes
     };
 
-- 
GitLab


From f127fa290c53c8e5ed1425b3b6965927ac971841 Mon Sep 17 00:00:00 2001
From: echicken <echicken@bbs.electronicchicken.com>
Date: Fri, 14 Aug 2020 22:51:11 -0400
Subject: [PATCH 569/752] Make the script not poop its pants if the requested
 file directory or library does not exist. (Thx DM)

---
 web/pages/.examples/002-files.xjs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web/pages/.examples/002-files.xjs b/web/pages/.examples/002-files.xjs
index 2175252c76..b0e510aaa0 100644
--- a/web/pages/.examples/002-files.xjs
+++ b/web/pages/.examples/002-files.xjs
@@ -4,7 +4,7 @@
 	load(settings.web_lib + 'request.js');
 	load(settings.web_lib + 'files.js');
     locale.section = 'page_files';
-	if (Request.has_param('dir')) {
+	if (Request.has_param('dir') && file_area.dir[Request.get_param('dir')]) {
 ?>
 	<ol class="breadcrumb">
 		<li>
@@ -57,7 +57,7 @@
 	<?xjs listFiles(Request.get_param('dir')).forEach(writeFileDetails); ?>
 	</div>
 
-<?xjs } else if (Request.has_param('library')) { ?>
+<?xjs } else if (Request.has_param('library') && file_area.lib_list[Request.get_param('library')]) { ?>
 
 	<ol class="breadcrumb">
 		<li>
-- 
GitLab


From ef16c941c590d61ad576e51a0651f7646b8d995b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 15 Aug 2020 17:31:31 -0700
Subject: [PATCH 570/752] Don't allow downloads form non-accessible
 directories.

Double-check that the user has access to both the directory and the containing
library before allowing a download a file.
---
 web/root/api/files.ssjs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web/root/api/files.ssjs b/web/root/api/files.ssjs
index f128ffbaea..00f08a3b73 100644
--- a/web/root/api/files.ssjs
+++ b/web/root/api/files.ssjs
@@ -16,6 +16,8 @@ if ((http_request.method === 'GET' || http_request.method === 'POST') &&
 		case 'download-file':
 			if (typeof http_request.query.dir !== 'undefined' &&
 				typeof file_area.dir[http_request.query.dir[0]] !== 'undefined'	&&
+                file_area.dir[http_request.query.dir[0]].lib_index >= 0 &&
+                file_area.dir[http_request.query.dir[0]].index >= 0 &&
 				file_area.dir[http_request.query.dir[0]].can_download &&
 				typeof http_request.query.file !== 'undefined'
 			) {
-- 
GitLab


From a3c0f031ac017b95093a21f7bc6e75b25e56a6e4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 00:43:35 -0700
Subject: [PATCH 571/752] Deleting a file's extended description would try to
 delete some other file too

This is a "forever bug". I guess at one time I stored each file's extended
description in a separate file? I don't know, but this remove() call is
clearly wrong.
---
 src/sbbs3/listfile.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/sbbs3/listfile.cpp b/src/sbbs3/listfile.cpp
index f237cbd4d1..241a8d0461 100644
--- a/src/sbbs3/listfile.cpp
+++ b/src/sbbs3/listfile.cpp
@@ -1184,8 +1184,7 @@ int sbbs_t::listfileinfo(uint dirnum, char *filespec, long mode)
 						break;
 					if(f.misc&FM_EXTDESC) {
 						if(!noyes(text[DeleteExtDescriptionQ])) {
-							remove(str);
-							f.misc&=~FM_EXTDESC; } 
+							f.misc&=~FM_EXTDESC; }
 					}
 					if(!dir_op(dirnum)) {
 						putfiledat(&cfg,&f);
-- 
GitLab


From 22e07494530c8a3a2cc5328c5a3a77224c2d5c6e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 10:57:43 -0700
Subject: [PATCH 572/752] Make the default node notification error level
 "critical".

---
 node1/node.cnf | Bin 3504 -> 2170 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/node1/node.cnf b/node1/node.cnf
index 79d7a50a007232d7cd9f9d6b8dd03bd43ecb24c7..194e4ce6ff25dca6d0c6e04cad2ab3d1ee7215d0 100644
GIT binary patch
delta 40
wcmdlW{YzlOdPa6vtC-@Xq~giz87(KjXLMjKNzE;YnH<Pmy?H&;DON@%06$$0umAu6

literal 3504
zcmZQ%@XJq0RWM{A3?d>TbfGwcuxbJ-gqj!_7#Wxds3IuM$e_bWP!ZJ>s4C`U7MCcf
zrcupAQVohpE-A{1AypSby`G+aNoqxjK0<^vR*0)_00q{QW*HXM&=}Cui%Cf=NsLJ?
zD#|a?%gIm2q7<tTn*Q|CqEb?{Gob0u$xqMEE7r>?F2QQssNi4=0aeAqyi^JzOI5Le
zBK?CcJVxC!8Umz*04oD4gQ{Y2a$35AYPN!YiE241x*=+}(6!En*f~l<Lg4=>7||iX
zz|8Q2DS;D`7#u^?+=2~k4T6nq4UH^Z4Z;i}Ow^nW)m)4q^0X!$LtG8Ri0OIYaEN1w
zO9&1rqJ$kod<{G(vfq&?dvGgaWng4x0QF%c8H^1KAnI5dHn8L|7#czOTnq`E&lrqM
Gpj-e)RzX|<

-- 
GitLab


From 3bdee85d6dadaff15cb7874e8bf5bf93877b199e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 11:00:07 -0700
Subject: [PATCH 573/752] Ignore some files and directories that are created as
 a result of running SCFG on the repo.

---
 .gitignore | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/.gitignore b/.gitignore
index 2e419f4a25..9f4db0b363 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,8 @@ src/sbbs3/ctrl/*.tds
 src/sbbs3/ctrl/sbbsctrl.mak
 3rdp/src
 localdefs.mk
+node2
+node3
+node4
+*.?.cnf
+ctrl/recycle
-- 
GitLab


From c2b64f0228de63cdb70404c63ecd039d23bf91f0 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 11:01:03 -0700
Subject: [PATCH 574/752] Update a lot of help text and fix a couple cosmetic
 issues.

---
 src/sbbs3/scfg/scfgnode.c | 62 ++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 30 deletions(-)

diff --git a/src/sbbs3/scfg/scfgnode.c b/src/sbbs3/scfg/scfgnode.c
index 003d0d5c82..0a6b4f1498 100644
--- a/src/sbbs3/scfg/scfgnode.c
+++ b/src/sbbs3/scfg/scfgnode.c
@@ -55,9 +55,16 @@ void node_menu()
 		if(savnode)
 			j|=WIN_PASTE;
 		uifc.helpbuf=
-			"`Node List:`\n"
+			"`Nodes:`\n"
 			"\n"
-			"This is the list of configured nodes in your system.\n"
+			"This is the list of configured terminal server nodes. A node is required\n"
+			"for each supported simultaneous 'caller'.\n"
+			"\n"
+			"`Note:` The `FirstNode` (e.g. Node 1) configuration settings are used for\n"
+			"      all the nodes supported by a single terminal server instance.\n"
+			"\n"
+			"`Note:` When nodes are added to this list, the `LastNode` value must be\n"
+			"      adjusted accordingly. See the `ctrl/sbbs.ini` file for more details.\n"
 			"\n"
 			"To add a node, hit ~ INS ~.\n"
 			"\n"
@@ -184,12 +191,11 @@ void node_cfg()
 		opt[i][0]=0;
 		sprintf(str,"Node %d Configuration",cfg.node_num);
 		uifc.helpbuf=
-			"`Node Configuration Menu:`\n"
+			"`Node Configuration:`\n"
 			"\n"
-			"This is the node configuration menu. The options available from this\n"
-			"menu will only affect the selected node's configuration.\n"
-			"\n"
-			"Options with a trailing `...` will produce a sub-menu of more options.\n"
+			"The configuration settings of the `FirstNode` will determine the behavior\n"
+			"of all nodes of a single terminal server instance  (through `LastNode`).\n"
+			"See the `ctrl/sbbs.ini` file for details.\n"
 		;
 		switch(uifc.list(WIN_ACT|WIN_CHE|WIN_BOT|WIN_RHT,0,0,60,&node_dflt,0
 			,str,opt)) {
@@ -205,8 +211,9 @@ void node_cfg()
 			case 0:
 				uifc.helpbuf=
 					"`Node Phone Number:`\n"
-					"This is the phone number to access the selected node (e.g. for SEXPOTS).\n"
-					"This value is used for documentary purposes only.\n"
+					"\n"
+					"This is the phone number to access the selected node (e.g. for `SEXPOTS`).\n"
+					"This value is used for information purposes only.\n"
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,10,"Phone Number",cfg.node_phone,sizeof(cfg.node_phone)-1,K_EDIT);
 				break;
@@ -238,7 +245,7 @@ void node_cfg()
 						"This is the toggle options menu for the selected node's configuration.\n"
 						"\n"
 						"The available options from this menu can all be toggled between two or\n"
-						"more states, such as `Yes` and `No``\n"
+						"more states, such as `Yes` and `No``.\n"
 					;
 					switch(uifc.list(WIN_BOT|WIN_RHT|WIN_ACT|WIN_SAV,3,2,35,&tog_dflt
 						,&tog_bar,"Toggle Options",opt)) {
@@ -250,8 +257,8 @@ void node_cfg()
 							uifc.helpbuf=
 								"`Allow Login by User Number:`\n"
 								"\n"
-								"If you want users to be able login using their user number at the `NN:`\n"
-								"set this option to `Yes`.\n"
+								"If you want users to be able login using their user number at the\n"
+								"login prompt, set this option to `Yes`.\n"
 							;
 							i=uifc.list(WIN_MID|WIN_SAV,0,10,0,&i,0
 								,"Allow Login by User Number",uifcYesNoOpts);
@@ -289,7 +296,7 @@ void node_cfg()
 								"`Always Prompt for Password:`\n"
 								"\n"
 								"If you want to have attempted logins using an unknown user name still\n"
-								"prompt for a password, set this option to `Yes`.\n"
+								"prompt for a password (i.e. for enhanced security), set this option to `Yes`.\n"
 							;
 							i=uifc.list(WIN_MID|WIN_SAV,0,10,0,&i,0
 								,"Always Prompt for Password",uifcYesNoOpts);
@@ -327,8 +334,8 @@ void node_cfg()
 							uifc.helpbuf=
 								"`Spinning Pause Prompt:`\n"
 								"\n"
-								"If you want to have a spinning cursor at the [Hit a key] prompt, set\n"
-								"this option to `Yes`.\n"
+								"If you want to display a spinning cursor at the [Hit a key] prompt,\n"
+								"set this option to `Yes`.\n"
 							;
 							i=uifc.list(WIN_MID|WIN_SAV,0,10,0,&i,0
 								,"Spinning Cursor at Pause Prompt",uifcYesNoOpts);
@@ -411,7 +418,7 @@ void node_cfg()
 								"whom the feedback is sent. The normal value of this option is `1` for\n"
 								"user number one.\n"
 							;
-							uifc.input(WIN_MID,0,13,"Validation User Number (0=Nobody)"
+							uifc.input(WIN_MID|WIN_SAV,0,13,"Validation User Number (0=Nobody)"
 								,str,4,K_NUMBER|K_EDIT);
 							cfg.node_valuser=atoi(str);
 							break;
@@ -427,13 +434,13 @@ void node_cfg()
 								"\n"
 								"Note: error messages are always logged as well (e.g. to `data/error.log`)."
 							;
-							uifc.input(WIN_MID,0,13,"Notification User Number (0=Nobody)"
+							uifc.input(WIN_MID|WIN_SAV,0,13,"Notification User Number (0=Nobody)"
 								,str,4,K_NUMBER|K_EDIT);
 							cfg.node_erruser=atoi(str);
 							break;
 						case __COUNTER__:
 							uifc.helpbuf=
-								"~ Notification Error Level ~\n"
+								"`Notification Error Level`\n"
 								"\n"
 								"Select the minimum severity of error messages that should be forwarded\n"
 								"to the Notification User. The normal setting would be `Critical`.";
@@ -451,7 +458,7 @@ void node_cfg()
 								"`Semaphore Check Frequency While Waiting for Call (in seconds):`\n"
 								"\n"
 								"This is the number of seconds between semaphore checks while this node\n"
-								"is waiting for a caller. Default is `60` seconds.\n"
+								"is waiting for a caller. Default is `5` seconds.\n"
 							;
 							uifc.input(WIN_MID|WIN_SAV,0,14
 								,"Seconds Between Semaphore Checks"
@@ -464,7 +471,7 @@ void node_cfg()
 								"`Statistics Check Frequency While Waiting for Call (in seconds):`\n"
 								"\n"
 								"This is the number of seconds between static checks while this node\n"
-								"is waiting for a caller. Default is `10` seconds.\n"
+								"is waiting for a caller. Default is `5` seconds.\n"
 							;
 							uifc.input(WIN_MID|WIN_SAV,0,14
 								,"Seconds Between Statistic Checks"
@@ -501,14 +508,12 @@ void node_cfg()
 							uifc.helpbuf=
 								"`Daily Event:`\n"
 								"\n"
-								"If you have an event that this node should run every day, enter the\n"
-								"command line for that event here.\n"
-								"\n"
-								"An event can be any valid DOS command line. If multiple programs or\n"
-								"commands are required, use a batch file.\n"
+								"If you have an event that this node's terminal server should run every\n"
+								"day, enter the command line for that event here.\n"
 								"\n"
-								"Remember: The `%!` command line specifier is an abbreviation for your\n"
-								"         configured `EXEC` directory path.\n"
+								"An event can be any valid command line. If multiple programs or commands\n"
+								"are required, use a batch file or shell script.\n"
+								SCFG_CMDLINE_SPEC_HELP
 							;
 							uifc.input(WIN_MID|WIN_SAV,0,10,"Daily Event"
 								,cfg.node_daily,sizeof(cfg.node_daily)-1,K_EDIT);
@@ -522,9 +527,6 @@ void node_cfg()
 								"disk or other volatile media. This directory contains the system's menus\n"
 								"and other important text files, so be sure the files and directories are\n"
 								"moved to this directory if you decide to change it.\n"
-								"\n"
-								"This option allows you to change the location of your control directory.\n"
-								"The `/text/`` suffix (sub-directory) cannot be changed or removed.\n"
 							;
 							uifc.input(WIN_MID|WIN_SAV,0,10,"Text Directory"
 								,cfg.text_dir,sizeof(cfg.text_dir)-1,K_EDIT);
-- 
GitLab


From cf42236fd52153a9a05ba5d8c9edb9627c2080c4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 13:11:42 -0700
Subject: [PATCH 575/752] Enable UTF-8 characters in QWK packets created for
 VERT by default.

---
 ctrl/msgs.cnf | Bin 20927 -> 20927 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/ctrl/msgs.cnf b/ctrl/msgs.cnf
index 53553ac1f11825ce5c40fa9c2e66d86575b58c90..111df8a276867c33e5a9318cd4270136333e360a 100644
GIT binary patch
delta 18
acmdnLm~sDN#tpaqCl|Wj*}Oe~gAo8&WC)1>

delta 18
acmdnLm~sDN#tpaqCr{?Qvw3>}2O|Ji&<Jk;

-- 
GitLab


From 8ac7aa6a55b4ea9691aeda546b2ebbcb508f0e78 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 13:13:03 -0700
Subject: [PATCH 576/752] Ignore the data directory (created when running
 SCFG).

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 9f4db0b363..267e253b5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ src/sbbs3/ctrl/*.tds
 src/sbbs3/ctrl/sbbsctrl.mak
 3rdp/src
 localdefs.mk
+data
 node2
 node3
 node4
-- 
GitLab


From 7ea751f78e04f94e96b8b5c4dd51146283bfb28d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 13:15:08 -0700
Subject: [PATCH 577/752] Add more cmd-line specifiers (e.g. %g, %j, %k) to
 online help.

---
 src/sbbs3/scfg/scfg.h | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/sbbs3/scfg/scfg.h b/src/sbbs3/scfg/scfg.h
index 101c5fa2f9..bd3f89ec62 100644
--- a/src/sbbs3/scfg/scfg.h
+++ b/src/sbbs3/scfg/scfg.h
@@ -50,18 +50,22 @@
 #define SCFG_CMDLINE_SPEC_HELP 	"\n"																				\
 								"The following is a list of commonly-used command-line specifiers:\n"				\
 								"\n"																				\
-								"  `%f`  The path/filename of the file to act upon or door/game drop file\n"		\
+								"  `%f`  The path/filename of the file to act upon or door/game `drop file`\n"		\
 								"  `%s`  File specification (e.g. `*.txt`) or the current `Startup Directory`\n"	\
-								"  `%!`  The Synchronet `exec` directory (use `%@` for non-Unix only)\n"			\
 								"  `%.`  Executable file extension (`.exe`, or blank for Unix systems)\n"			\
-								"  `%n`  The current node directory\n"												\
-								"  `%#`  The current node number\n"													\
-								"  `%a`  The current user's alias\n"												\
-								"  `%1`  The current user's number (use `%2`, `%3`, etc. for 0-padded values)\n"	\
-								"  `%h`  The current TCP/IP socket descriptor (handle) value\n"						\
+								"  `%!`  The Synchronet `exec directory` (use `%@` for non-Unix only)\n"			\
+								"  `%g`  The Synchronet `temp directory`\n"											\
+								"  `%j`  The Synchronet `data directory`\n"											\
+								"  `%k`  The Synchronet `ctrl directory`\n"											\
+								"  `%z`  The Synchronet `text directory`\n"											\
+								"  `%n`  The current `node directory`\n"											\
+								"  `%#`  The current `node number`\n"												\
+								"  `%a`  The current `user's alias`\n"												\
+								"  `%1`  The current `user's number` (use `%2`, `%3`, etc. for 0-padded values)\n"	\
+								"  `%h`  The current TCP/IP `socket` descriptor (handle) value\n"					\
 								"  `%p`  The current connection type (protocol, e.g. `telnet`, `rlogin`, etc.)\n"	\
-								"  `%r`  The current user's terminal height (rows)\n"								\
-								"  `%w`  The current user's terminal width (columns)\n"								\
+								"  `%r`  The current user's terminal height (`rows`)\n"								\
+								"  `%w`  The current user's terminal width (`columns`)\n"							\
 								"\n"																				\
 								"For a complete list of the supported command-line specifiers, see:\n"				\
 								"`http://wiki.synchro.net/config:cmdline`\n"
-- 
GitLab


From 02e06427acae84ea2cbb0df1c991aa013dcb594b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 13:16:30 -0700
Subject: [PATCH 578/752] Cosmetic fixes/improvements.

---
 src/sbbs3/scfg/scfgnet.c | 48 +++++++++++++++-------
 src/sbbs3/scfg/scfgsys.c | 87 +++++++++++++++++++++++-----------------
 2 files changed, 84 insertions(+), 51 deletions(-)

diff --git a/src/sbbs3/scfg/scfgnet.c b/src/sbbs3/scfg/scfgnet.c
index baac2a4787..eb7eb4f386 100644
--- a/src/sbbs3/scfg/scfgnet.c
+++ b/src/sbbs3/scfg/scfgnet.c
@@ -225,7 +225,7 @@ void net_cfg()
 								"`QWK Network Hubs:`\n"
 								"\n"
 								"This is a list of QWK network hubs that your system calls to exchange\n"
-								"packets with.\n"
+								"message packets with.\n"
 								"\n"
 								"To add a hub, select the desired location with the arrow keys and hit\n"
 								"~ INS ~.\n"
@@ -245,7 +245,7 @@ void net_cfg()
 									"`QWK Network Hub System ID:`\n"
 									"\n"
 									"This is the QWK System ID of this hub. It is used for incoming and\n"
-									"outgoing network packets and must be accurate.\n"
+									"outgoing network message packets and must be accurate.\n"
 								;
 								if(uifc.input(WIN_MID|WIN_SAV,0,0
 									,"System ID",str,LEN_QWKID,K_UPPER)<1)
@@ -444,6 +444,9 @@ void net_cfg()
 							"This is a filename that will be used as a semaphore (signal) to your\n"
 							"FidoNet front-end that new NetMail has been created and the messages\n"
 							"should be re-scanned.\n"
+							"\n"
+							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"NetMail Semaphore"
 							,cfg.netmail_sem,sizeof(cfg.netmail_sem)-1,K_EDIT);
@@ -455,6 +458,9 @@ void net_cfg()
 							"This is a filename that will be used as a semaphore (signal) to your\n"
 							"FidoNet front-end that new EchoMail has been created and the messages\n"
 							"should be re-scanned.\n"
+							"\n"
+							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"EchoMail Semaphore"
 							,cfg.echomail_sem,sizeof(cfg.echomail_sem)-1,K_EDIT);
@@ -470,7 +476,7 @@ void net_cfg()
 							,cfg.netmail_dir,sizeof(cfg.netmail_dir)-1,K_EDIT);
 						break;
 					case 5:
-						i=0;
+						i = (cfg.netmail_misc & NMAIL_ALLOW) ? 0 : 1;
 						uifc.helpbuf=
 							"`Allow Users to Send NetMail:`\n"
 							"\n"
@@ -489,7 +495,7 @@ void net_cfg()
 						}
 						break;
 					case 6:
-						i=0;
+						i = (cfg.netmail_misc & NMAIL_FILE) ? 0 : 1;
 						uifc.helpbuf=
 							"`Allow Users to Send NetMail File Attachments:`\n"
 							"\n"
@@ -508,7 +514,7 @@ void net_cfg()
 						}
 						break;
 					case 7:
-						i=1;
+						i = (cfg.netmail_misc & NMAIL_ALIAS) ? 0 : 1;
 						uifc.helpbuf=
 							"`Use Aliases in NetMail:`\n"
 							"\n"
@@ -529,7 +535,7 @@ void net_cfg()
 						}
 						break;
 					case 8:
-						i=1;
+						i = (cfg.netmail_misc & NMAIL_CRASH) ? 0 : 1;
 						uifc.helpbuf=
 							"`NetMail Defaults to Crash Status:`\n"
 							"\n"
@@ -548,7 +554,7 @@ void net_cfg()
 						}
 						break;
 					case 9:
-						i=1;
+						i = (cfg.netmail_misc & NMAIL_DIRECT) ? 0 : 1;
 						uifc.helpbuf=
 							"`NetMail Defaults to Direct Status:`\n"
 							"\n"
@@ -567,7 +573,7 @@ void net_cfg()
 						}
 						break;
 					case 10:
-						i=1;
+						i = (cfg.netmail_misc & NMAIL_HOLD) ? 0 : 1;
 						uifc.helpbuf=
 							"`NetMail Defaults to Hold Status:`\n"
 							"\n"
@@ -586,7 +592,7 @@ void net_cfg()
 						}
 						break;
 					case 11:
-						i=0;
+						i = (cfg.netmail_misc & NMAIL_KILL) ? 0 : 1;
 						uifc.helpbuf=
 							"`Kill NetMail After it is Sent:`\n"
 							"\n"
@@ -618,7 +624,7 @@ void net_cfg()
 						cfg.netmail_cost=atol(str);
 						break; 
 					case 13:
-						i=0;
+						i = (cfg.netmail_misc & NMAIL_CHSRCADDR) ? 0 : 1;
 						uifc.helpbuf=
 							"`Choose NetMail Source Address:`\n"
 							"\n"
@@ -694,6 +700,9 @@ void net_cfg()
 							"This is a filename that will be used as a semaphore (signal) to any\n"
 							"external Internet e-mail processors that new mail has been received\n"
 							"and the message base should be re-scanned.\n"
+							"\n"
+							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"Inbound Semaphore"
 							,cfg.smtpmail_sem,sizeof(cfg.smtpmail_sem)-1,K_EDIT);
@@ -705,12 +714,15 @@ void net_cfg()
 							"This is a filename that will be used as a semaphore (signal) to any\n"
 							"external Internet gateways (if supported) that new mail has been created\n"
 							"and the message base should be re-scanned.\n"
+							"\n"
+							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"Outbound Semaphore"
 							,cfg.inetmail_sem,sizeof(cfg.inetmail_sem)-1,K_EDIT);
 						break;
 					case 3:
-						i=0;
+						i = (cfg.inetmail_misc & NMAIL_ALLOW) ? 0 : 1;
 						uifc.helpbuf=
 							"`Allow Users to Send Internet E-mail:`\n"
 							"\n"
@@ -729,7 +741,7 @@ void net_cfg()
 						}
 						break;
 					case 4:
-						i=0;
+						i = (cfg.inetmail_misc & NMAIL_FILE) ? 0 : 1;
 						uifc.helpbuf=
 							"`Allow Users to Send Internet E-mail File Attachments:`\n"
 							"\n"
@@ -748,7 +760,7 @@ void net_cfg()
 						}
 						break;
 					case 5:
-						i=1;
+						i = (cfg.inetmail_misc & NMAIL_ALIAS) ? 0 : 1;
 						uifc.helpbuf=
 							"`Use Aliases in Internet E-mail:`\n"
 							"\n"
@@ -769,7 +781,7 @@ void net_cfg()
 						}
 						break;
 					case 6:
-						i=0;
+						i = (cfg.inetmail_misc & NMAIL_KILL) ? 0 : 1;
 						uifc.helpbuf=
 							"`Kill Internet E-mail After it is Sent:`\n"
 							"\n"
@@ -879,6 +891,11 @@ void qhub_edit(int num)
 			"found in Kludge Lines and also addresses the 25-character QWK field\n"
 			"length limits. HEADERS.DAT is supported in Synchronet v3.15 and later.\n"
 			"\n"
+			"Synchronet v3.18 and later supports `UTF-8` encoded messages within QWK\n"
+			"packets. If the hub is using Synchronet v3.18 or later, set this option\n"
+			"to `Yes`. This option also changes the QWK new-line sequence to the ASCII\n"
+			"LF (10) character instead of the traditional QWK newline byte (227).\n"
+			"\n"
 			"`Extended (QWKE) Packets` are not normally used in QWK Networking.\n"
 			"Setting this to `Yes` enables some QWKE-specific Kludge Lines that are\n"
 			"superfluous when the HEADERS.DAT file is supported and used.\n"
@@ -920,6 +937,7 @@ void qhub_edit(int num)
 					"\n"
 					"This is the command line to use to create (compress) REP packets for\n"
 					"this QWK network hub.\n"
+					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,""
 					,cfg.qhub[num]->pack,sizeof(cfg.qhub[num]->pack)-1,K_EDIT);
@@ -930,6 +948,7 @@ void qhub_edit(int num)
 					"\n"
 					"This is the command line to use to extract (decompress) QWK packets from\n"
 					"this QWK network hub.\n"
+					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,""
 					,cfg.qhub[num]->unpack,sizeof(cfg.qhub[num]->unpack)-1,K_EDIT);
@@ -940,6 +959,7 @@ void qhub_edit(int num)
 					"\n"
 					"This is the command line to use to initiate a call-out to this QWK\n"
 					"network hub.\n"
+					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,""
 					,cfg.qhub[num]->call,sizeof(cfg.qhub[num]->call)-1,K_EDIT);
diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index c023a35b31..f2e009333c 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -410,7 +410,7 @@ void sys_cfg(void)
 				uifc.input(WIN_MID,0,0,"System Password",cfg.sys_pass,sizeof(cfg.sys_pass)-1,K_EDIT|K_UPPER);
 				break;
 			case 5:
-				i=1;
+				i = (cfg.sys_misc&SM_PWEDIT) ? 0 : 1;
 				uifc.helpbuf=
 					"`Allow Users to Change Their Password:`\n"
 					"\n"
@@ -428,26 +428,26 @@ void sys_cfg(void)
 					cfg.sys_misc&=~SM_PWEDIT;
 					uifc.changes=1; 
 				}
-				i=0;
+				i = cfg.sys_pwdays ? 0 : 1;
 				uifc.helpbuf=
-					"`Force Periodic Password uifc.changes:`\n"
+					"`Force Periodic New Password:`\n"
 					"\n"
-					"If you want your users to be forced to change their passwords\n"
-					"periodically, select `Yes`.\n"
+					"If you want your users to be forced to have a new password periodically,\n"
+					"select `Yes`.\n"
 				;
 				i=uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,0
-					,"Force Periodic Password Changes",opt);
+					,"Force Periodic New Password",uifcYesNoOpts);
 				if(!i) {
 					ultoa(cfg.sys_pwdays,str,10);
 				uifc.helpbuf=
-					"`Maximum Days Between Password uifc.changes:`\n"
+					"`Maximum Days Between New Passwords:`\n"
 					"\n"
-					"Enter the maximum number of days allowed between password uifc.changes.\n"
+					"Enter the maximum number of days allowed between password changes.\n"
 					"If a user has not voluntarily changed his or her password in this\n"
 					"many days, he or she will be forced to change their password upon\n"
 					"logon.\n"
 				;
-					uifc.input(WIN_MID,0,0,"Maximum Days Between Password Changes"
+					uifc.input(WIN_MID,0,0,"Maximum Days Between New Password"
 						,str,5,K_NUMBER|K_EDIT);
 					cfg.sys_pwdays=atoi(str); 
 				}
@@ -476,7 +476,7 @@ void sys_cfg(void)
 				uifc.helpbuf=
 					"`Maximum Days of Inactivity Before Auto-Deletion:`\n"
 					"\n"
-					"If you want users that haven't logged on in certain period of time to\n"
+					"If you want users that have not logged-on in a certain period of time to\n"
 					"be automatically deleted, set this value to the maximum number of days\n"
 					"of inactivity before the user is deleted. Setting this value to `0`\n"
 					"disables this feature.\n"
@@ -491,9 +491,9 @@ void sys_cfg(void)
 				uifc.helpbuf=
 					"`New User Password:`\n"
 					"\n"
-					"If you want callers to only be able to logon as `New` if they know a\n"
-					"certain password, enter that password here. If you want any caller to\n"
-					"be able to logon as New, leave this option blank.\n"
+					"If you want callers to only be able to logon as `New` ~ only ~ if they know\n"
+					"a secret password, enter that password here.  If you prefer any caller\n"
+					"be able to logon as `New`, leave this option blank.\n"
 				;
 				uifc.input(WIN_MID,0,0,"New User Password",cfg.new_pass,sizeof(cfg.new_pass)-1
 					,K_EDIT|K_UPPER);
@@ -612,7 +612,8 @@ void sys_cfg(void)
 							uifc.helpbuf=
 								"`Allow Sysop Logins:`\n"
 								"\n"
-								"If you want to be able to login with sysop access, set this option to `Yes`.\n"
+								"If you want to be able to login with system operator access, set this\n"
+								"option to `Yes`.\n"
 							;
 							i=uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,0
 								,"Allow Sysop Logins",uifcYesNoOpts);
@@ -952,6 +953,8 @@ void sys_cfg(void)
 								"`New User Exemption Flags:`\n"
 								"\n"
 								"These are the exemptions that are automatically given to new users.\n"
+								"\n"
+								"See `http://wiki.synchro.net/access:exemptions` for details.\n"
 							;
 							uifc.input(WIN_SAV|WIN_MID,0,0,"Exemption Flags",str,26
 								,K_EDIT|K_UPPER|K_ALPHA);
@@ -963,6 +966,8 @@ void sys_cfg(void)
 								"`New User Restriction Flags:`\n"
 								"\n"
 								"These are the restrictions that are automatically given to new users.\n"
+								"\n"
+								"See `http://wiki.synchro.net/access:restrictions` for details.\n"
 							;
 							uifc.input(WIN_SAV|WIN_MID,0,0,"Restriction Flags",str,26
 								,K_EDIT|K_UPPER|K_ALPHA);
@@ -1084,8 +1089,8 @@ void sys_cfg(void)
 								"settings on this menu. The user can then change them to his or her\n"
 								"liking.\n"
 								"\n"
-								"See the Synchronet User Manual for more information on the individual\n"
-								"options available.\n"
+								"See the Synchronet User Manual (`http://synchro.net/docs/user.html`)\n"
+								"for more information on the individual options available.\n"
 							;
 							j=0;
 							k=0;
@@ -1128,7 +1133,7 @@ void sys_cfg(void)
 									,"Auto Hang-up After Xfer"
 									,cfg.new_misc&AUTOHANG ? "Yes":"No");
 								opt[i][0]=0;
-								j=uifc.list(WIN_BOT|WIN_RHT,2,1,0,&j,&k
+								j=uifc.list(WIN_BOT|WIN_RHT|WIN_SAV,2,1,0,&j,&k
 									,"Default Toggle Options",opt);
 								if(j==-1)
 									break;
@@ -1240,7 +1245,7 @@ void sys_cfg(void)
 									,"Color Terminal"
 									,cfg.uq&UQ_COLORTERM ? "Yes":"No");
 								opt[i][0]=0;
-								j=uifc.list(WIN_BOT|WIN_RHT,2,1,0,&j,&k
+								j=uifc.list(WIN_BOT|WIN_RHT|WIN_SAV,2,1,0,&j,&k
 									,"New User Questions",opt);
 								if(j==-1)
 									break;
@@ -1374,7 +1379,7 @@ void sys_cfg(void)
 								"Think of it as a password to guarantee that new users read the text\n"
 								"displayed to them.\n"
 							;
-							uifc.input(WIN_MID,0,0,"New User Magic Word",cfg.new_magic,sizeof(cfg.new_magic)-1
+							uifc.input(WIN_MID|WIN_SAV,0,0,"New User Magic Word",cfg.new_magic,sizeof(cfg.new_magic)-1
 								,K_EDIT|K_UPPER);
 							break;
 						case 1:
@@ -1385,6 +1390,8 @@ void sys_cfg(void)
 								"This directory must be located where `ALL` nodes can access it and\n"
 								"`MUST NOT` be placed on a RAM disk or other volatile media.\n"
 								"\n"
+								"See `http://wiki.synchro.net/dir:data` for details.\n"
+								"\n"
 								"This option allows you to change the location of your data directory.\n"
 							;
 							strcpy(str,cfg.data_dir);
@@ -1399,6 +1406,8 @@ void sys_cfg(void)
 								"`Log File Directory:`\n"
 								"\n"
 								"Log files will be stored in this directory.\n"
+								"\n"
+								"By default, this is set to the same as your Data File directory.\n"
 							;
 							strcpy(str,cfg.logs_dir);
 							if(uifc.input(WIN_MID|WIN_SAV,0,9,"Logs Directory"
@@ -1411,12 +1420,15 @@ void sys_cfg(void)
 							uifc.helpbuf=
 								"`Executable/Module File Directory:`\n"
 								"\n"
-								"The Synchronet exec directory contains executable files that your BBS\n"
-								"executes. This directory does `not` need to be in your DOS search path.\n"
+								"The Synchronet exec directory contains program and script files that the\n"
+								"BBS executes. This directory does `not` need to be in your OS search path.\n"
+								"\n"
 								"If you place programs in this directory for the BBS to execute, you\n"
-								"should place the `%!` abbreviation for the exec directory at the\n"
+								"should place the `%!` specifier for the `exec` directory at the\n"
 								"beginning of the configured command-lines.\n"
 								"\n"
+								"See `http://wiki.synchro.net/dir:exec` for details.\n"
+								"\n"
 								"This option allows you to change the location of your exec directory.\n"
 							;
 							strcpy(str,cfg.exec_dir);
@@ -1431,12 +1443,17 @@ void sys_cfg(void)
 								"`Modified Modules Directory:`\n"
 								"\n"
 								"This optional directory can be used to specify a location where modified\n"
-								"module files are stored. These modified modules will take precedence over\n"
-								"stock modules with the same filename (in the exec directory) and will\n"
-								"not be overwritten by future updates/upgrades.\n"
+								"module files are stored. These modified modules will take precedence\n"
+								"over modules with the same filename (in the `exec` directory) and will\n"
+								"`not be overwritten` by future updates/upgrades.\n"
+								"\n"
+								"Sub-directory searches of this directory also take precedence\n"
+								"(e.g. `mods/load/*` overrides `exec/load/*`).\n"
 								"\n"
-								"If this directory is `blank`, then this feature is not used and all modules\n"
-								"are assumed to be located in the `exec` directory.\n"
+								"If this directory is `blank`, then this feature is not used and all\n"
+								"modules are assumed to be located in the `exec` directory.\n"
+								"\n"
+								"See `http://wiki.synchro.net/dir:mods` for details.\n"
 							;
 							strcpy(str,cfg.mods_dir);
 							if(uifc.input(WIN_MID|WIN_SAV,0,9,"Mods Directory"
@@ -1483,9 +1500,9 @@ void sys_cfg(void)
 								"\n"
 								"This is the monetary value of a credit (How many credits per dollar).\n"
 								"This value should be a power of 2 (1, 2, 4, 8, 16, 32, 64, 128, etc.)\n"
-								"since credits are usually converted by 100 kilobyte (102400) blocks.\n"
-								"To make a dollar worth two megabytes of credits, set this value to\n"
-								"2,097,152 (a megabyte is 1024*1024 or 1048576).\n"
+								"since credits are usually converted in 100 kibibyte (102400) blocks.\n"
+								"To make a dollar worth two mebibytes of credits, set this value to\n"
+								"2,097,152 (a mebibyte is 1024*1024 or 1048576).\n"
 							;
 							ultoa(cfg.cdt_per_dollar,str,10);
 							uifc.input(WIN_MID|WIN_SAV,0,0
@@ -1668,15 +1685,15 @@ void sys_cfg(void)
 						"`Scan Msgs`    Executed when a user reads or scans a message sub-board\n"
 						"`Scan Subs`    Executed when a user scans one or more sub-boards for msgs\n"
 						"`List Msgs`    Executed when a user lists msgs from the msg read prompt\n"
-						"`List Logons`  Executed when a user lists logons (i.e. '-y' for yesterday)\n"
+						"`List Logons`  Executed when a user lists logons ('-y' for yesterday)\n"
 						"`List Nodes`   Executed when a user lists all nodes\n"
 						"`Who's Online` Executed when a user lists the nodes in-use (e.g. `^U`)\n"
 						"`Private Msg`  Executed when a user sends a private node msg (e.g. `^P`)\n"
 						"\n"
 						"`Note:` JavaScript modules take precedence over Baja modules if both exist\n"
-						"in your `exec` or `mods` directories.\n"
+						"      in your `exec` or `mods` directories.\n"
 					;
-					switch(uifc.list(WIN_ACT|WIN_T2B|WIN_RHT,0,0,32,&k,0
+					switch(uifc.list(WIN_ACT|WIN_T2B|WIN_RHT,0,0,40,&k,0
 						,"Loadable Modules",opt)) {
 
 						case -1:
@@ -2060,10 +2077,6 @@ void sys_cfg(void)
 						"user's expiration date may be extended and additional credits may also\n"
 						"be added using quick-validation sets.\n"
 						"\n"
-						"Holding down ~ ALT ~ and one of the number keys (`1-9`) while a user\n"
-						"is online, automatically sets his or user security values to the\n"
-						"quick-validation set for that number key.\n"
-						"\n"
 						"From within the `User Edit` function, a sysop can use the `V`alidate\n"
 						"User command and select from this quick-validation list to change a\n"
 						"user's security values with very few key-strokes.\n"
-- 
GitLab


From a8b05c31f57542e3c8f28442f7f119be08cdc682 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 13:29:24 -0700
Subject: [PATCH 579/752] Set default Maximum QWK Message Age to Unlimited.
 Allows a new BBS to import all of DOVE-Net by default. This probably should
 be a separate setting for users and QWKnet hubs.

---
 ctrl/msgs.cnf | Bin 20927 -> 20927 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/ctrl/msgs.cnf b/ctrl/msgs.cnf
index 111df8a276867c33e5a9318cd4270136333e360a..c8d90a0b44b920381dc87f6054baf72b58f50948 100644
GIT binary patch
delta 31
ncmdnLm~sDN#t9~y86CJ8CokYUJ-L8YVY33$1IEo<EL(j6zHbXN

delta 33
pcmdnLm~sDN#t9~ja+?_)xEUuO;5aq8fK_3$0@DM=&0H*7eF4gE3vd7c

-- 
GitLab


From 702aa153ea85024bca53c90afe3a6880629f9a6c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 14:20:05 -0700
Subject: [PATCH 580/752] Online help documents the '*' and '?' cmdline
 prefixes

---
 src/sbbs3/scfg/scfg.c     |  2 +-
 src/sbbs3/scfg/scfg.h     |  9 +++++++--
 src/sbbs3/scfg/scfgchat.c |  2 ++
 src/sbbs3/scfg/scfgmsg.c  | 16 ++++++++++++----
 src/sbbs3/scfg/scfgnet.c  | 11 +++++++----
 src/sbbs3/scfg/scfgnode.c |  1 +
 src/sbbs3/scfg/scfgxfr1.c | 20 ++++++++++----------
 src/sbbs3/scfg/scfgxfr2.c |  9 ++++++---
 src/sbbs3/scfg/scfgxtrn.c | 21 +++++++++++++++------
 9 files changed, 61 insertions(+), 30 deletions(-)

diff --git a/src/sbbs3/scfg/scfg.c b/src/sbbs3/scfg/scfg.c
index 33bed347a5..d34e90c431 100644
--- a/src/sbbs3/scfg/scfg.c
+++ b/src/sbbs3/scfg/scfg.c
@@ -1010,7 +1010,7 @@ void shell_cfg()
 			"\n"
 			"This is a list of `Command Shells` configured for your system.\n"
 			"Command shells are the programmable command and menu structures which\n"
-			"are available for your BBS.\n"
+			"are available for the users of your BBS's terminal server.\n"
 			"\n"
 			"To add a command shell section, select the desired location with the\n"
 			"arrow keys and hit ~ INS ~.\n"
diff --git a/src/sbbs3/scfg/scfg.h b/src/sbbs3/scfg/scfg.h
index bd3f89ec62..c3992d81ed 100644
--- a/src/sbbs3/scfg/scfg.h
+++ b/src/sbbs3/scfg/scfg.h
@@ -47,8 +47,14 @@
 
 #define SETHELP(where)  uifc.sethelp(where)
 
+#define SCFG_CMDLINE_PREFIX_HELP "\n"																				\
+								"Command lines may begin with a special `prefix` character to indicate:\n"			\
+								"\n"																				\
+								"  `*`   Program is either a JavaScript (`.js`) or Baja (`.bin`) module\n"			\
+								"  `?`   Program is a JavaScript (`.js`) module\n"
+
 #define SCFG_CMDLINE_SPEC_HELP 	"\n"																				\
-								"The following is a list of commonly-used command-line specifiers:\n"				\
+								"The following is a list of commonly-used command line specifiers:\n"				\
 								"\n"																				\
 								"  `%f`  The path/filename of the file to act upon or door/game `drop file`\n"		\
 								"  `%s`  File specification (e.g. `*.txt`) or the current `Startup Directory`\n"	\
@@ -70,7 +76,6 @@
 								"For a complete list of the supported command-line specifiers, see:\n"				\
 								"`http://wiki.synchro.net/config:cmdline`\n"
 
-
 /*************/
 /* Constants */
 /*************/
diff --git a/src/sbbs3/scfg/scfgchat.c b/src/sbbs3/scfg/scfgchat.c
index 44cb9b026d..f1953c31c4 100644
--- a/src/sbbs3/scfg/scfgchat.c
+++ b/src/sbbs3/scfg/scfgchat.c
@@ -75,6 +75,7 @@ void page_cfg()
 				"`External Chat Pager Command Line:`\n"
 				"\n"
 				"This is the command line to execute for this external chat pager.\n"
+				SCFG_CMDLINE_PREFIX_HELP
 				SCFG_CMDLINE_SPEC_HELP
 			;
 			if(uifc.input(WIN_MID|WIN_SAV,0,0,"Command Line",str,50
@@ -145,6 +146,7 @@ void page_cfg()
 						"`External Chat Pager Command Line:`\n"
 						"\n"
 						"This is the command line to execute for this external chat pager.\n"
+						SCFG_CMDLINE_PREFIX_HELP
 						SCFG_CMDLINE_SPEC_HELP
 					;
 					strcpy(str,cfg.page[i]->cmd);
diff --git a/src/sbbs3/scfg/scfgmsg.c b/src/sbbs3/scfg/scfgmsg.c
index f47f924103..080b2572a8 100644
--- a/src/sbbs3/scfg/scfgmsg.c
+++ b/src/sbbs3/scfg/scfgmsg.c
@@ -1505,10 +1505,18 @@ void msg_opts()
 				uifc.helpbuf=
 					"`Extra Attribute Codes...`\n"
 					"\n"
-					"Synchronet can support the native text attribute codes of other BBS\n"
-					"programs in messages (menus, posts, e-mail, etc.) To enable the extra\n"
-					"attribute codes for another BBS program, set the corresponding option\n"
-					"to `Yes`.\n"
+					"Synchronet can support the native text attribute (e.g. color) codes of\n"
+					"other BBS programs in messages (menus, posts, e-mail, etc.) To enable\n"
+					"extra attribute codes for another BBS program, set the corresponding\n"
+					"option to `Yes`.\n"
+					"\n"
+					"- WWIV color codes are preceded by a Ctrl-C (ASCII 3) character.\n"
+					"- PCBoard color codes are of the form `@Xxx` where `xx` are hex digits.\n"
+					"- Wildcat color codes are of the form `@xx@` where `xx` are hex digits.\n"
+					"- Celerity color codes are of the form `|x` where `x` is an alpha char.\n"
+					"- Renegade color codes are of the form `|xx` where `xx` are decimal digits.\n"
+					"\n"
+					"See `http://wiki.synchro.net/custom:colors` for details.\n"
 				;
 
 				j=0;
diff --git a/src/sbbs3/scfg/scfgnet.c b/src/sbbs3/scfg/scfgnet.c
index eb7eb4f386..eb3ac852c1 100644
--- a/src/sbbs3/scfg/scfgnet.c
+++ b/src/sbbs3/scfg/scfgnet.c
@@ -445,7 +445,7 @@ void net_cfg()
 							"FidoNet front-end that new NetMail has been created and the messages\n"
 							"should be re-scanned.\n"
 							"\n"
-							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							"`Command line specifiers may be included in the semaphore filename.`\n"
 							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"NetMail Semaphore"
@@ -459,7 +459,7 @@ void net_cfg()
 							"FidoNet front-end that new EchoMail has been created and the messages\n"
 							"should be re-scanned.\n"
 							"\n"
-							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							"`Command line specifiers may be included in the semaphore filename.`\n"
 							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"EchoMail Semaphore"
@@ -701,7 +701,7 @@ void net_cfg()
 							"external Internet e-mail processors that new mail has been received\n"
 							"and the message base should be re-scanned.\n"
 							"\n"
-							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							"`Command line specifiers may be included in the semaphore filename.`\n"
 							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"Inbound Semaphore"
@@ -715,7 +715,7 @@ void net_cfg()
 							"external Internet gateways (if supported) that new mail has been created\n"
 							"and the message base should be re-scanned.\n"
 							"\n"
-							"`Command-line specifiers may be included in the semaphore filename.`\n"
+							"`Command line specifiers may be included in the semaphore filename.`\n"
 							SCFG_CMDLINE_SPEC_HELP
 						;
 						uifc.input(WIN_MID|WIN_SAV,0,0,"Outbound Semaphore"
@@ -937,6 +937,7 @@ void qhub_edit(int num)
 					"\n"
 					"This is the command line to use to create (compress) REP packets for\n"
 					"this QWK network hub.\n"
+					SCFG_CMDLINE_PREFIX_HELP
 					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,""
@@ -948,6 +949,7 @@ void qhub_edit(int num)
 					"\n"
 					"This is the command line to use to extract (decompress) QWK packets from\n"
 					"this QWK network hub.\n"
+					SCFG_CMDLINE_PREFIX_HELP
 					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,""
@@ -959,6 +961,7 @@ void qhub_edit(int num)
 					"\n"
 					"This is the command line to use to initiate a call-out to this QWK\n"
 					"network hub.\n"
+					SCFG_CMDLINE_PREFIX_HELP
 					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,""
diff --git a/src/sbbs3/scfg/scfgnode.c b/src/sbbs3/scfg/scfgnode.c
index 0a6b4f1498..d51a250ab4 100644
--- a/src/sbbs3/scfg/scfgnode.c
+++ b/src/sbbs3/scfg/scfgnode.c
@@ -513,6 +513,7 @@ void node_cfg()
 								"\n"
 								"An event can be any valid command line. If multiple programs or commands\n"
 								"are required, use a batch file or shell script.\n"
+								SCFG_CMDLINE_PREFIX_HELP
 								SCFG_CMDLINE_SPEC_HELP
 							;
 							uifc.input(WIN_MID|WIN_SAV,0,10,"Daily Event"
diff --git a/src/sbbs3/scfg/scfgxfr1.c b/src/sbbs3/scfg/scfgxfr1.c
index 0e91d51d4c..7c13509770 100644
--- a/src/sbbs3/scfg/scfgxfr1.c
+++ b/src/sbbs3/scfg/scfgxfr1.c
@@ -337,7 +337,7 @@ void xfer_opts()
 									,cfg.fview[i]->ext,sizeof(cfg.fview[i]->ext)-1,K_EDIT);
 								break;
 							case 1:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.fview[i]->cmd,sizeof(cfg.fview[i]->cmd)-1,K_EDIT);
@@ -468,7 +468,7 @@ void xfer_opts()
 									,cfg.ftest[i]->ext,sizeof(cfg.ftest[i]->ext)-1,K_EDIT);
 								break;
 							case 1:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.ftest[i]->cmd,sizeof(cfg.ftest[i]->cmd)-1,K_EDIT);
@@ -602,7 +602,7 @@ void xfer_opts()
 									,cfg.dlevent[i]->ext,sizeof(cfg.dlevent[i]->ext)-1,K_EDIT);
 								break;
 							case 1:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.dlevent[i]->cmd,sizeof(cfg.dlevent[i]->cmd)-1,K_EDIT);
@@ -727,7 +727,7 @@ void xfer_opts()
 									,cfg.fextr[i]->ext,sizeof(cfg.fextr[i]->ext)-1,K_EDIT);
 								break;
 							case 1:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.fextr[i]->cmd,sizeof(cfg.fextr[i]->cmd)-1,K_EDIT);
@@ -845,7 +845,7 @@ void xfer_opts()
 									,cfg.fcomp[i]->ext,sizeof(cfg.fcomp[i]->ext)-1,K_EDIT);
 								break;
 							case 1:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.fcomp[i]->cmd,sizeof(cfg.fcomp[i]->cmd)-1,K_EDIT);
@@ -998,31 +998,31 @@ void xfer_opts()
 								getar(str,cfg.prot[i]->arstr);
 								break;
 							case 3:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.prot[i]->ulcmd,sizeof(cfg.prot[i]->ulcmd)-1,K_EDIT);
 								break;
 							case 4:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.prot[i]->dlcmd,sizeof(cfg.prot[i]->dlcmd)-1,K_EDIT);
 								break;
 							case 5:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.prot[i]->batulcmd,sizeof(cfg.prot[i]->batulcmd)-1,K_EDIT);
 								break;
 							case 6:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.prot[i]->batdlcmd,sizeof(cfg.prot[i]->batdlcmd)-1,K_EDIT);
 								break;
 							case 7:
-								uifc.helpbuf = SCFG_CMDLINE_SPEC_HELP;
+								uifc.helpbuf = SCFG_CMDLINE_PREFIX_HELP SCFG_CMDLINE_SPEC_HELP;
 								uifc.input(WIN_MID|WIN_SAV,0,0
 									,"Command"
 									,cfg.prot[i]->bicmd,sizeof(cfg.prot[i]->bicmd)-1,K_EDIT);
diff --git a/src/sbbs3/scfg/scfgxfr2.c b/src/sbbs3/scfg/scfgxfr2.c
index ed2887efcd..69eadb4bcf 100644
--- a/src/sbbs3/scfg/scfgxfr2.c
+++ b/src/sbbs3/scfg/scfgxfr2.c
@@ -1272,7 +1272,7 @@ void dir_cfg(uint libnum)
 						"the directory based on the date the file was uploaded or last\n"
 						"downloaded (If the `Purge by Last Download` toggle option is used).\n"
 						"\n"
-						"The Synchronet file base maintenance program (`DELFILES`) must be used\n"
+						"The Synchronet file base maintenance program (`delfiles`) must be used\n"
 						"to automatically remove files based on age.\n"
 					;
 					uifc.input(WIN_MID|WIN_SAV,0,17,"Maximum Age of Files (in days)"
@@ -1746,7 +1746,7 @@ void dir_cfg(uint libnum)
 								uifc.helpbuf=
 									"`Purge Files Based on Date of Last Download:`\n"
 									"\n"
-									"Using the Synchronet file base maintenance utility (`DELFILES`), you can\n"
+									"Using the Synchronet file base maintenance utility (`delfiles`), you can\n"
 									"have files removed based on the number of days since last downloaded\n"
 									"rather than the number of days since the file was uploaded (default),\n"
 									"by setting this option to `Yes`.\n"
@@ -1911,7 +1911,10 @@ void dir_cfg(uint libnum)
 									"`Upload Semaphore File:`\n"
 									"\n"
 									"This is a filename that will be used as a semaphore (signal) to your\n"
-									"FidoNet front-end that new files are ready to be hatched for export.\n"
+									"FidoNet software that new files are ready to be hatched for export.\n"
+									"\n"
+									"`Command line specifiers may be included in the semaphore filename.`\n"
+									SCFG_CMDLINE_SPEC_HELP
 								;
 								uifc.input(WIN_MID|WIN_SAV,0,17,"Upload Semaphore"
 									,cfg.dir[i]->upload_sem,sizeof(cfg.dir[i]->upload_sem)-1,K_EDIT);
diff --git a/src/sbbs3/scfg/scfgxtrn.c b/src/sbbs3/scfg/scfgxtrn.c
index 93e038c27b..84a75a8d8b 100644
--- a/src/sbbs3/scfg/scfgxtrn.c
+++ b/src/sbbs3/scfg/scfgxtrn.c
@@ -44,11 +44,11 @@ static char* use_shell_opt = "Use Shell / New Context";
 static char* use_shell_help =
 	"`Use System Shell or New JavaScript Context to Execute:`\n"
 	"\n"
-	"If this command-line requires the system command shell to execute\n"
+	"If this command line requires the system command shell to execute\n"
 	"(e.g. uses pipes/redirection or invokes a Unix shell script or\n"
 	"DOS/Windows batch/command file), then set this option to ~Yes~.\n"
 	"\n"
-	"If this command-line is invoking a Synchronet JavaScript module\n"
+	"If this command line is invoking a Synchronet JavaScript module\n"
 	"(e.g. it begins with a '`?`' character), then setting this option to ~Yes~\n"
 	"will enable the creation and initialization of a new JavaScript run-time\n"
 	"context for it to execute within, for every invocation."
@@ -313,8 +313,8 @@ void xprogs_cfg()
 		uifc.helpbuf=
 			"`Online External Programs:`\n"
 			"\n"
-			"From this menu, you can configure external events, external editors, or\n"
-			"online external programs (doors).\n"
+			"From this menu, you can configure external events, external message\n"
+			"editors, or online external programs (e.g. `door games`).\n"
 		;
 		switch(uifc.list(WIN_ORG|WIN_CHE|WIN_ACT,0,0,0,&xprogs_dflt,0
 			,"External Programs",opt)) {
@@ -383,6 +383,7 @@ void fevents_cfg()
 					"in the logon sequence of users that includes interaction or requires\n"
 					"account information, you probably want to use an online external\n"
 					"program configured to run as a logon event.\n"
+					SCFG_CMDLINE_PREFIX_HELP
 					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,"Logon Event"
@@ -398,6 +399,7 @@ void fevents_cfg()
 					"wish to have a program execute before carrier is dropped, you probably\n"
 					"want to use an `Online External Program` configured to run as a logoff\n"
 					"event.\n"
+					SCFG_CMDLINE_PREFIX_HELP
 					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,"Logout Event"
@@ -409,6 +411,7 @@ void fevents_cfg()
 					"\n"
 					"This is the command line for a program that will run after the first\n"
 					"user that logs on after midnight, logs off (regardless of what node).\n"
+					SCFG_CMDLINE_PREFIX_HELP
 					SCFG_CMDLINE_SPEC_HELP
 				;
 				uifc.input(WIN_MID|WIN_SAV,0,0,"Daily Event"
@@ -584,6 +587,7 @@ void tevents_cfg()
 						"`Timed Event Command Line:`\n"
 						"\n"
 						"This is the command line to execute upon this timed event.\n"
+						SCFG_CMDLINE_PREFIX_HELP
 						SCFG_CMDLINE_SPEC_HELP
 					;
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Command"
@@ -1122,6 +1126,7 @@ void xtrn_cfg(uint section)
 						"`Online Program Command Line:`\n"
 						"\n"
 						"This is the command line to execute to run the online program.\n"
+						SCFG_CMDLINE_PREFIX_HELP
 						SCFG_CMDLINE_SPEC_HELP
 					;
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Command"
@@ -1133,6 +1138,7 @@ void xtrn_cfg(uint section)
 						"\n"
 						"This is the command line to execute after the main command line. This\n"
 						"option is usually only used for multiuser online programs.\n"
+						SCFG_CMDLINE_PREFIX_HELP
 						SCFG_CMDLINE_SPEC_HELP
 					;
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Clean-up"
@@ -1373,8 +1379,9 @@ void xtrn_cfg(uint section)
 						"Set this option to ~Yes~ if you would like an automatic screen pause\n"
 						"(`[Hit a key]` prompt) to appear after the program executes.\n"
 						"\n"
-						"This can be useful if the program displays information just before exiting\n"
-						"or you want to debug a program with a program not running correctly.\n"
+						"This can be useful if the program displays information just before\n"
+						"exiting or you want to debug a program with a program not running\n"
+						"correctly.\n"
 					;
 					k=uifc.list(WIN_MID|WIN_SAV,0,0,0,&k,0
 						,"Pause After Execution",uifcYesNoOpts);
@@ -1769,6 +1776,7 @@ void xedit_cfg()
 						"`External Editor Command Line:`\n"
 						"\n"
 						"This is the command line to execute when using this editor.\n"
+						SCFG_CMDLINE_PREFIX_HELP
 						SCFG_CMDLINE_SPEC_HELP
 					;
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Command"
@@ -2543,6 +2551,7 @@ void hotkey_cfg(void)
 						"`Hot Key Event Command Line:`\n"
 						"\n"
 						"This is the command line to execute when this hot key is pressed.\n"
+						SCFG_CMDLINE_PREFIX_HELP
 						SCFG_CMDLINE_SPEC_HELP
 					;
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Command"
-- 
GitLab


From 435e79167fd306a2f55c731a1d9bca739cc47aba Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 16:57:05 -0700
Subject: [PATCH 581/752] Add -force and -share options, handle missing
 protocol property.

---
 exec/sbbslist.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/exec/sbbslist.js b/exec/sbbslist.js
index 10f2d286ca..8108135694 100644
--- a/exec/sbbslist.js
+++ b/exec/sbbslist.js
@@ -736,8 +736,8 @@ function verify_bbs(bbs)
     for(i in bbs.service) {
         if(js.terminated)
             break;
-        var protocol = bbs.service[i].protocol.toLowerCase();
-        if(protocol != "telnet" && protocol != "rlogin")
+        var protocol = bbs.service[i].protocol;
+        if(!protocol || (protocol.toLowerCase() != "telnet" && protocol.toLowerCase() != "rlogin"))
             continue;
         bbs.entry.autoverify.attempts++;
         var result = verify_terminal_service(bbs.service[i]);
@@ -2327,6 +2327,9 @@ function main()
 			case "-exclude":
 				exclude.push(val);
 				break;
+			case "-force":
+				export_freq = 0;
+				break;
 			case "-format":
 				if(val === undefined || val === '?' || !val.length) {
 					print("Supported list formats:");
@@ -2578,6 +2581,9 @@ function main()
 						break;
 				}
 				break;
+			case "share":
+				print(lfexpand(JSON.stringify(lib.share_list(list, optval[cmd] == "qwk"), null, 1)));
+				break;
 			case "add":
 				if(lib.system_exists(list, system.name)) {
 					alert("System '" + system.name + "' already exists");
-- 
GitLab


From 15f76ba8f52ae4cec080650bec6422da304a496c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:18:11 -0700
Subject: [PATCH 582/752] Add Lizard Master's Pickles LORD (.js) IGM

---
 xtrn/lord/pickle/LICENSE    | 674 ++++++++++++++++++++++++++++++++++++
 xtrn/lord/pickle/README.md  |  13 +
 xtrn/lord/pickle/garden.ans |   6 +
 xtrn/lord/pickle/pickle.ans |  20 ++
 xtrn/lord/pickle/pickle.js  | 146 ++++++++
 5 files changed, 859 insertions(+)
 create mode 100644 xtrn/lord/pickle/LICENSE
 create mode 100644 xtrn/lord/pickle/README.md
 create mode 100644 xtrn/lord/pickle/garden.ans
 create mode 100644 xtrn/lord/pickle/pickle.ans
 create mode 100644 xtrn/lord/pickle/pickle.js

diff --git a/xtrn/lord/pickle/LICENSE b/xtrn/lord/pickle/LICENSE
new file mode 100644
index 0000000000..f288702d2f
--- /dev/null
+++ b/xtrn/lord/pickle/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/xtrn/lord/pickle/README.md b/xtrn/lord/pickle/README.md
new file mode 100644
index 0000000000..59462f2d2a
--- /dev/null
+++ b/xtrn/lord/pickle/README.md
@@ -0,0 +1,13 @@
+# otherplacespickles
+Other Places Pickles is an IGM for Lord 5.00 JS by 
+The Lizard Master
+
+See LORD 5.00 JS Sysop.doc for installing this and other IGMs
+
+In this IGM you find the Pickle Goddess and her magic pickle
+garden.  The magic pickles are stronger the higher you select
+from the patch.  You gain or lose a percentage of what you 
+have from a randomly selected trait.  The trick to this is
+you can keep eating stronger pickles, but the deck is stacked
+in a Las Vegas slot machine way.  On a long enough time line,
+the house will win so as not to upset the balance of the game.
diff --git a/xtrn/lord/pickle/garden.ans b/xtrn/lord/pickle/garden.ans
new file mode 100644
index 0000000000..56412e94bc
--- /dev/null
+++ b/xtrn/lord/pickle/garden.ans
@@ -0,0 +1,6 @@
+ ��������������������������������������������������
+ �111112222233333444445555566666777777888888999999�
+ �111112222233333444445555566666777777888888999999�
+ �111112222233333444445555566666777777888888999999�
+ �111112222233333444445555566666777777888888999999�
+ �������������PICKLE'S MAGIC GARDEN����������������
\ No newline at end of file
diff --git a/xtrn/lord/pickle/pickle.ans b/xtrn/lord/pickle/pickle.ans
new file mode 100644
index 0000000000..d3e821cd50
--- /dev/null
+++ b/xtrn/lord/pickle/pickle.ans
@@ -0,0 +1,20 @@
+
+������������ �������
+��������۲��� ����۲�
+����������������۲��۰
+�����������۲���������۱
+������������������۲�����۲
+����������������۲�����������
+�����������������������������
+����۲�����������������۲����
+�����������������۲�����
+�����������������۱� ���۰
+����۲�����������۱�   ���۰
+�������۲��������۲������
+������۲�������۱� ��۰
+�����۲�����۲���۲   �   �
+����۲����۱��۲� ��    �
+��۲���۱���
+���������  �  �
+����
+�
\ No newline at end of file
diff --git a/xtrn/lord/pickle/pickle.js b/xtrn/lord/pickle/pickle.js
new file mode 100644
index 0000000000..bee5ba2e59
--- /dev/null
+++ b/xtrn/lord/pickle/pickle.js
@@ -0,0 +1,146 @@
+function menu() {
+        sclrscr();
+		sln('');
+		sln('');
+		lln('  `5Welcome Other Places Pickles\'s!');
+		lln('  `2Home of the Pickle Goddess!');
+		sln('');
+        display_file(js.exec_dir+'pickle.ans');
+		more_nomail();
+        var runonce = false;
+        do {
+            sclrscr();
+            display_file(js.exec_dir+'garden.ans');
+            lln('  `5The Pickle Goddess has decided to allow you to');
+            lln('  `5pick a Pickle from her garden! But choose wisely,');
+            lln('  `5Not all are great choices!  The higher you go,');
+            lln('  `5The more potent the pickle!');
+            if (runonce) { lln('\n`2Must be a choice from 1-9!'); }
+            lln('\n`2Which row would you like to pick from?');
+            sw('  ');
+            tempnum = parseInt(getstr(0, 0, 10, 0, 7, '', {integer:true}), 10);
+            runonce = true;
+        } while (isNaN(tempnum) || tempnum < 1 || tempnum > 10);
+        sclrscr();
+		sln('');
+		sln('');
+        var randNum = random(100) + 1; 
+        var strType;
+        var strWhat;
+        var truncated;
+        var pctGainLost;
+    
+        pctGainLost = '.0' + tempnum;
+     
+        if (randNum >= 49) // Vegas slot machine odds, tilting to the house
+         strType='Bad'
+        else   
+         strType='Good';
+        var randNum = random(10) + 1;
+	    if (randNum == 1) {
+		 strWhat='hit points';
+         truncated=parseInt(player.hp * parseFloat(pctGainLost));
+            if (strType=='Bad') 
+                player.hp=player.hp-truncated;
+            else
+                player.hp=player.hp+truncated;
+        }
+		else if (randNum == 2) {
+	 	 strWhat='max hitpoints';
+         truncated=parseInt(player.hp_max * parseFloat(pctGainLost));
+            if (strType=='Bad') 
+                player.hp_max=player.hp_max-truncated;
+            else
+                player.hp_max=player.hp_max+truncated;  
+        }
+		else if (randNum == 3) {
+	 	 strWhat='forest fights';
+         truncated=parseInt(player.forest_fights * parseFloat(pctGainLost));
+            if (strType=='Bad') 
+                player.forest_fights=player.forest_fights-truncated;
+            else
+                player.forest_fights=player.forest_fights+truncated;              
+        }
+		else if (randNum == 4) {
+	 	 strWhat='gold';
+         truncated=parseInt(player.gold * parseFloat(pctGainLost));
+            if (strType=='Bad') 
+                player.gold=player.gold-truncated;
+            else
+                player.gold=player.gold+truncated;              
+        }
+		else if (randNum == 5) {
+	 	 strWhat='bank gold';
+         truncated=parseInt(player.bank * parseFloat(pctGainLost));
+             if (strType=='Bad') 
+                player.bank=player.bank-truncated;
+            else
+                player.bank=player.bank+truncated;              
+        }
+		else if (randNum == 6) {
+	 	 strWhat='defense';
+         truncated=parseInt(player.def * parseFloat(pctGainLost));
+             if (strType=='Bad') 
+                player.def=player.def-truncated;
+            else
+                player.def=player.def+truncated;              
+        }
+		else if (randNum == 7) {
+	 	 strWhat='strength';
+         truncated=parseInt(player.str * parseFloat(pctGainLost));
+             if (strType=='Bad') 
+                player.str=player.str-truncated;
+            else
+                player.str=player.str+truncated;                 
+        }
+		else if (randNum == 8) {
+	 	 strWhat='charm';
+         truncated=parseInt(player.cha * parseFloat(pctGainLost));
+             if (strType=='Bad') 
+                player.cha=player.cha-truncated;
+            else
+                player.cha=player.cha+truncated;              
+        }
+		else if (randNum == 9) {
+	 	 strWhat='gems';
+         truncated=parseInt(player.gem * parseFloat(pctGainLost));
+             if (strType=='Bad') 
+                player.gem=player.gem-truncated;
+            else
+                player.gem=player.gem+truncated;              
+        }
+		else{
+	 	 strWhat='des1';
+         truncated=1;
+        }
+        if (parseFloat(truncated) != 0) {
+            if (strType=='Good' && strWhat!='des1') {
+            lln('  `5This pickle is divine! Your '+strWhat+' increased by ' +truncated+'!');
+            truncated=parseInt(player.hp * parseFloat(pctGainLost));
+            }
+            else if (strType=='Bad' && strWhat!='des1'){		
+            lln('  `5This pickle taste like poo! Your '+strWhat+' decreased by ' +truncated+'!');
+            truncated=parseInt(player.hp * parseFloat(pctGainLost));
+            }
+            else {
+            lln('  `5You have been marked by tattoo from the Pickle Goddess!'); 
+            player.des1='I have been tagged pwned by the Pickle Goddess!';
+            }
+        }
+        else
+            {
+            lln('  `5You were about to... ');
+                if (strType=='Good')
+                    lw('  `5gain ');
+             else
+                 lw('  `5lose ');
+             lln('something, but you do not have enough ' + strWhat + '!'); 
+             lln('  `5Come back later when you level up or have more stuff... ');
+            }
+        more_nomail();
+		exit(0);
+}
+
+// possible record changes: hp, hp_max, forest_fights, gold, bank, def, str, cha, gem, des1
+
+menu();
-- 
GitLab


From 085de5b8463e49b592577a3c8546c4ff47469747 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:24:54 -0700
Subject: [PATCH 583/752] Set ANSI as execution requirement for doors that
 require ANSI.

Also, add the Pickels IGM to the LORD installer.

It's *really* cool that many of the full-screen/color JS games do
work (mostly) fine with PETSCII. Some still need a little TLC in
this area, but its way better than assuming or requiring ANSI.
---
 xtrn/druglord/install-xtrn.ini | 1 +
 xtrn/fatfish/install-xtrn.ini  | 1 +
 xtrn/knk/install-xtrn.ini      | 1 +
 xtrn/lord/install-xtrn.ini     | 6 +++++-
 xtrn/lord2/install-xtrn.ini    | 1 +
 xtrn/startrek/install-xtrn.ini | 1 +
 xtrn/tbd/install-xtrn.ini      | 1 +
 7 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/xtrn/druglord/install-xtrn.ini b/xtrn/druglord/install-xtrn.ini
index 60a2343b60..1e648a5f1c 100644
--- a/xtrn/druglord/install-xtrn.ini
+++ b/xtrn/druglord/install-xtrn.ini
@@ -8,6 +8,7 @@ Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 09:13:48 rswindell Exp $
 [prog:DRUGLORD]
 cmd  = ?druglord.js
 settings = XTRN_MULTIUSER
+execution_ars = ANSI
 required = true
 
 !include install-json-service.ini
diff --git a/xtrn/fatfish/install-xtrn.ini b/xtrn/fatfish/install-xtrn.ini
index b0dd5f1234..0d8b2eb729 100644
--- a/xtrn/fatfish/install-xtrn.ini
+++ b/xtrn/fatfish/install-xtrn.ini
@@ -8,6 +8,7 @@ Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:05:27 rswindell Exp $
 [prog:FATFISH]
 cmd  = ?fatfish.js
 settings = XTRN_MULTIUSER
+execution_ars = ANSI
 required = true
 
 !include install-json-service.ini
diff --git a/xtrn/knk/install-xtrn.ini b/xtrn/knk/install-xtrn.ini
index 6dbb45b62c..cf3c508a80 100644
--- a/xtrn/knk/install-xtrn.ini
+++ b/xtrn/knk/install-xtrn.ini
@@ -10,3 +10,4 @@ Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 09:20:20 rswindell Exp $
 [prog:KNK]
 cmd  = ?knk.js
 settings = XTRN_MULTIUSER
+execution_ars = ANSI
diff --git a/xtrn/lord/install-xtrn.ini b/xtrn/lord/install-xtrn.ini
index 10acf56beb..5911cf7ea3 100644
--- a/xtrn/lord/install-xtrn.ini
+++ b/xtrn/lord/install-xtrn.ini
@@ -11,6 +11,7 @@ Inst: $Id: install-xtrn.ini,v 1.4 2020/04/21 04:41:22 rswindell Exp $
 [prog:LORD]
 cmd  = ?lord
 ars  = NOT GUEST
+execution_ars = ANSI
 settings = XTRN_MULTIUSER
 required = true
 
@@ -36,4 +37,7 @@ prompt = Install LORD IGM: The Grab Bag
 prompt = Install LORD IGM: Aragorn's Timer
 
 [exec:lord.js +IGM oorphans/oorphans.js]
-prompt = Install Olodrin's Orphanage
+prompt = Install LORD IGM: Olodrin's Orphanage
+
+[exec:lord.js +IGM pickle/pickle.js]
+prompt = Install LORD IGM: Pickle's Magic Garden
diff --git a/xtrn/lord2/install-xtrn.ini b/xtrn/lord2/install-xtrn.ini
index e5b6f00648..cdb9697ac8 100644
--- a/xtrn/lord2/install-xtrn.ini
+++ b/xtrn/lord2/install-xtrn.ini
@@ -11,4 +11,5 @@ Inst: $Id: install-xtrn.ini,v 1.3 2020/04/21 04:41:44 rswindell Exp $
 [prog:LORD2]
 cmd  = ?lord2
 settings = XTRN_MULTIUSER
+execution_ars = ANSI
 required = true
diff --git a/xtrn/startrek/install-xtrn.ini b/xtrn/startrek/install-xtrn.ini
index bdecc20f82..d85475e03e 100644
--- a/xtrn/startrek/install-xtrn.ini
+++ b/xtrn/startrek/install-xtrn.ini
@@ -7,6 +7,7 @@ Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 09:22:37 rswindell Exp $
 [prog:STARTREK]
 cmd  = ?startrek.js
 settings = XTRN_MULTIUSER
+required = true
 
 !include install-json-service.ini
 
diff --git a/xtrn/tbd/install-xtrn.ini b/xtrn/tbd/install-xtrn.ini
index 17e0eb124d..8271356f9e 100644
--- a/xtrn/tbd/install-xtrn.ini
+++ b/xtrn/tbd/install-xtrn.ini
@@ -7,5 +7,6 @@ Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:41:51 rswindell Exp $
 [prog:TBD]
 cmd  = tbd%. /times=10
 settings = XTRN_MULTIUSER|XTRN_NATIVE|XTRN_MODUSERDAT
+execution_ars = ANSI
 type = XTRN_SBBS
 required = true
-- 
GitLab


From 289d1281db8adc3c5db6d0b8355c596da9eb5eab Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:29:25 -0700
Subject: [PATCH 584/752] Installer meta data (for use with install-xtrn.js /
 xtrn-setup.js)

---
 xtrn/dpoker/install-xtrn.ini | 13 +++++++++++++
 xtrn/sbj/install-xtrn.ini    | 13 +++++++++++++
 2 files changed, 26 insertions(+)
 create mode 100644 xtrn/dpoker/install-xtrn.ini
 create mode 100644 xtrn/sbj/install-xtrn.ini

diff --git a/xtrn/dpoker/install-xtrn.ini b/xtrn/dpoker/install-xtrn.ini
new file mode 100644
index 0000000000..fc52169835
--- /dev/null
+++ b/xtrn/dpoker/install-xtrn.ini
@@ -0,0 +1,13 @@
+Name: Domain Poker
+Desc: Real-time 5 card draw poker
+By:   King Drafus (Allen Christiansen), Domain Entertainment
+Cats: Games
+Subs: Casino, Cards, Multiplayer, Native
+Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:41:51 rswindell Exp $
+
+[prog:DPOKER]
+cmd  = dpoker%. /l
+clean_cmd = dpoker%. /clean
+settings = XTRN_MULTIUSER|XTRN_NATIVE|XTRN_MODUSERDAT
+type = XTRN_SBBS
+required = true
diff --git a/xtrn/sbj/install-xtrn.ini b/xtrn/sbj/install-xtrn.ini
new file mode 100644
index 0000000000..1147a194c0
--- /dev/null
+++ b/xtrn/sbj/install-xtrn.ini
@@ -0,0 +1,13 @@
+Name: Synchronet Blackjack
+Desc: Multiplayer 21, the first game written for Synchronet
+By:   Digital Man (Rob Swindell)
+Cats: Games
+Subs: Casino, Cards, Multiplayer, Chat, Native
+Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:41:51 rswindell Exp $
+
+[prog:SBJ]
+cmd  = sbj%.
+clean_cmd = sbjclean%.
+settings = XTRN_MULTIUSER|XTRN_NATIVE|XTRN_MODUSERDAT
+type = XTRN_SBBS
+required = true
-- 
GitLab


From 71c0a587006a869966cf3973e2560eea02a91050 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:40:33 -0700
Subject: [PATCH 585/752] Clear the screen when terminating.

---
 exec/ftn-setup.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/exec/ftn-setup.js b/exec/ftn-setup.js
index 4ac78745c3..783ff9e04c 100644
--- a/exec/ftn-setup.js
+++ b/exec/ftn-setup.js
@@ -106,3 +106,4 @@ while (!js.terminated) {
 }
 
 frame.close();
+console.clear(BG_BLACK|LIGHTGRAY);
-- 
GitLab


From e127c998ba38c46a37b3c8baff6091b27bd8f1e5 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:41:13 -0700
Subject: [PATCH 586/752] Daily-builds are now a branch, not a tag, as is the
 way of Git

---
 exec/testbuild.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/exec/testbuild.js b/exec/testbuild.js
index 58f5df70de..e8e3913cfe 100755
--- a/exec/testbuild.js
+++ b/exec/testbuild.js
@@ -220,9 +220,9 @@ send_email(system.platform + " builds successful in " + elapsed_time(time() - st
 
 chdir(temp_dir);
 
-var comment = "Successful build on " + system_description;
-system.exec("git tag -m \"" + comment + "\" -a goodbuild_" + platform + "_" + date_str);
-system.exec("git push --tags");
+system.exec("git checkout -b dailybuild_" + platform);
+system.exec("git merge master");
+system.exec("git push --set-upstream origin dailybuild_" + platform);
 
 var dest = file_area.dir["sbbs"].path+archive;
 log(LOG_INFO,format("Copying %s to %s",archive,dest));
-- 
GitLab


From 998503395b4740db313469ddd5dc5b479d803a43 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:42:09 -0700
Subject: [PATCH 587/752] A little punctuation and spacign fix-ups wrt batch
 queues.

---
 ctrl/text.dat | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/ctrl/text.dat b/ctrl/text.dat
index 1dde3fe688..0caa879c81 100644
--- a/ctrl/text.dat
+++ b/ctrl/text.dat
@@ -330,16 +330,16 @@
 "\r\nDownload queue is empty.\r\n"                      273 DownloadQueueIsEmpty
 "\r\n\1-\1g\1hUpload Queue:\1n\1g"\                          274 UploadQueueLstHdr
 	"        Description\r\n\r\n\1n"
-"\1g\1h%2d: \1n\1g%s     %s\r\n"                            275 UploadQueueLstFmt
+"\1g\1h%2d: \1n\1g%-12s     %s\r\n"                          275 UploadQueueLstFmt
 "\r\n\1-\1g\1hDownload Queue:\1n\1g"\                        276 DownloadQueueLstHdr
 	"      Credits      Bytes        Time\r\n\r\n\1n"
-"\1g\1h%2d: \1n\1g%s %11.11s %11.11s     %s\r\n"            277 DownloadQueueLstFmt
+"\1g\1h%2d: \1n\1g%-12s %11.11s %11.11s     %s\r\n"       277 DownloadQueueLstFmt
 "\r\n\1w\1h         Totals: %11.11s %11.11s     %s\r\n"   278 DownloadQueueTotals
 "\r\n\1-\1gRemove which file from upload "\               279 RemoveWhichFromUlQueue
 	"queue (1-%d): \1n"
 "\r\n\1-\1gRemove which file from download "\             280 RemoveWhichFromDlQueue
 	"queue (1-%d): \1n"
-"\r\nUpload queue is empty\r\n"                         281 UploadQueueIsEmpty
+"\r\nUpload queue is empty.\r\n"                        281 UploadQueueIsEmpty
 "\r\nHang up after transfer"                            282 HangUpAfterXferQ
 "\r\n\1w\1hStart transfer now (Ctrl-X to abort):\r\n\1n"   283 StartXferNow
 "\1_\r\n\1b\1hDisconnecting @ELLIPSIS@ \1wH\1bang up or \1wA\1bbort "  284 Disconnecting
-- 
GitLab


From aeeda5a5f18cfb6c05227ee86178c61a619bd123 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:43:04 -0700
Subject: [PATCH 588/752] On a fresh install, this 'Removing unknown fmutex'
 error always happens

The file is state.bin.lock.
Just ignore it (let Deuce debug the reason why).
---
 xtrn/lord/lord.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xtrn/lord/lord.js b/xtrn/lord/lord.js
index b0cbbc719b..d39792bd53 100644
--- a/xtrn/lord/lord.js
+++ b/xtrn/lord/lord.js
@@ -1788,7 +1788,8 @@ function rfmutex(fname)
 	fname += '.lock';
 	idx = cleanup_files.indexOf(fname);
 	if (idx === -1) {
-		throw new Error('Removing unknown fmutex '+fname);
+	//	throw new Error('Removing unknown fmutex '+fname);
+		return;
 	}
 	file_remove(fname);
 	cleanup_files.splice(idx, 1);
-- 
GitLab


From 4c68693db59955f5da4ec35dd0113faea3931d0b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:46:04 -0700
Subject: [PATCH 589/752] Ignore exec/*.bin (compiled Baja modules).

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 267e253b5a..ad4669580a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,4 @@ node3
 node4
 *.?.cnf
 ctrl/recycle
+exec/*.bin
\ No newline at end of file
-- 
GitLab


From 78fd5c9a7792df163782ed6ce32e2fac506093ec Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 18:47:30 -0700
Subject: [PATCH 590/752] Someone committed a baja.js to the exec dir which
 broke this Makefile

Call 'baja.exe' intead of just 'baja', so Windows doesn't try to invoke baja.js
---
 exec/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/Makefile b/exec/Makefile
index 8ccf359135..03fdb7e5ce 100644
--- a/exec/Makefile
+++ b/exec/Makefile
@@ -31,4 +31,4 @@ all : 	\
 
 
 .src.bin:
-	@baja /q $<
+	@baja.exe /q $<
-- 
GitLab


From 0886bf3eb163971e5642dc0488c82348ede0eb7b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:19:32 -0700
Subject: [PATCH 591/752] Clear the last line (not the entire screen) when
 exiting.

---
 exec/ftn-setup.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/exec/ftn-setup.js b/exec/ftn-setup.js
index 783ff9e04c..d52a17a464 100644
--- a/exec/ftn-setup.js
+++ b/exec/ftn-setup.js
@@ -106,4 +106,5 @@ while (!js.terminated) {
 }
 
 frame.close();
-console.clear(BG_BLACK|LIGHTGRAY);
+console.creturn();
+console.cleartoeol();
-- 
GitLab


From bdb46b5c99a860a3a40910ed63bed14acb3b722c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:20:18 -0700
Subject: [PATCH 592/752] Increment version to v3.18b

---
 src/sbbs3/ctrl/sbbsctrl.bpr | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/ctrl/sbbsctrl.bpr b/src/sbbs3/ctrl/sbbsctrl.bpr
index d34814ae5b..05f779670b 100644
--- a/src/sbbs3/ctrl/sbbsctrl.bpr
+++ b/src/sbbs3/ctrl/sbbsctrl.bpr
@@ -125,7 +125,7 @@ IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=3
 MinorVer=18
-Release=0
+Release=1
 Build=0
 Debug=0
 PreRelease=0
@@ -138,13 +138,13 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Rob Swindell
 FileDescription=Synchronet BBS Control Panel
-FileVersion=3.18.0.0
+FileVersion=3.18.1.0
 InternalName=
 LegalCopyright=(C) 2020 Rob Swindell
 LegalTrademarks=
 OriginalFilename=sbbsctrl.exe
 ProductName=Synchronet BBS
-ProductVersion=3.18a
+ProductVersion=3.18b
 Comments=
 
 [Excluded Packages]
-- 
GitLab


From a8d516d636a72205927e274b035af9a914a0bdf7 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:20:43 -0700
Subject: [PATCH 593/752] Increment version to v3.18b.

---
 src/sbbs3/sbbsdefs.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index 285c972038..296b513155 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -50,7 +50,7 @@
 /*************/
 
 #define VERSION 	"3.18"  /* Version: Major.minor  */
-#define REVISION	'a'     /* Revision: lowercase letter */
+#define REVISION	'b'     /* Revision: lowercase letter */
 #define VERSION_NUM	(31800	 + (tolower(REVISION)-'a'))
 #define VERSION_HEX	(0x31800 + (tolower(REVISION)-'a'))
 
-- 
GitLab


From b5f1c4afdb847e476aab9e49f919041065cc50b8 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:22:42 -0700
Subject: [PATCH 594/752] Punctuation and white-space in batch queue-related
 strings.

---
 src/sbbs3/text_defaults.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/sbbs3/text_defaults.c b/src/sbbs3/text_defaults.c
index c20b074f9b..2abdb4139b 100644
--- a/src/sbbs3/text_defaults.c
+++ b/src/sbbs3/text_defaults.c
@@ -440,19 +440,19 @@ const char * const text_defaults[TOTAL_TEXT]={
 	,"\x0d\x0a\x44\x6f\x77\x6e\x6c\x6f\x61\x64\x20\x71\x75\x65\x75\x65\x20\x69\x73\x20\x65\x6d\x70\x74\x79\x2e\x0d\x0a" // 273 DownloadQueueIsEmpty
 	,"\x0d\x0a\x01\x2d\x01\x67\x01\x68\x55\x70\x6c\x6f\x61\x64\x20\x51\x75\x65\x75\x65\x3a\x01\x6e\x01\x67\x20\x20\x20\x20\x20\x20\x20"
 		"\x20\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x0d\x0a\x0d\x0a\x01\x6e" // 274 UploadQueueLstHdr
-	,"\x01\x67\x01\x68\x25\x32\x64\x3a\x20\x01\x6e\x01\x67\x25\x73\x20\x20\x20\x20\x20\x25\x73\x0d\x0a" // 275 UploadQueueLstFmt
+	,"\x01\x67\x01\x68\x25\x32\x64\x3a\x20\x01\x6e\x01\x67\x25\x2d\x31\x32\x73\x20\x20\x20\x20\x20\x25\x73\x0d\x0a" // 275 UploadQueueLstFmt
 	,"\x0d\x0a\x01\x2d\x01\x67\x01\x68\x44\x6f\x77\x6e\x6c\x6f\x61\x64\x20\x51\x75\x65\x75\x65\x3a\x01\x6e\x01\x67\x20\x20\x20\x20\x20"
 		"\x20\x43\x72\x65\x64\x69\x74\x73\x20\x20\x20\x20\x20\x20\x42\x79\x74\x65\x73\x20\x20\x20\x20\x20\x20\x20\x20\x54\x69\x6d\x65\x0d"
 		"\x0a\x0d\x0a\x01\x6e" // 276 DownloadQueueLstHdr
-	,"\x01\x67\x01\x68\x25\x32\x64\x3a\x20\x01\x6e\x01\x67\x25\x73\x20\x25\x31\x31\x2e\x31\x31\x73\x20\x25\x31\x31\x2e\x31\x31\x73\x20"
-		"\x20\x20\x20\x20\x25\x73\x0d\x0a" // 277 DownloadQueueLstFmt
+	,"\x01\x67\x01\x68\x25\x32\x64\x3a\x20\x01\x6e\x01\x67\x25\x2d\x31\x32\x73\x20\x25\x31\x31\x2e\x31\x31\x73\x20\x25\x31\x31\x2e\x31"
+		"\x31\x73\x20\x20\x20\x20\x20\x25\x73\x0d\x0a" // 277 DownloadQueueLstFmt
 	,"\x0d\x0a\x01\x77\x01\x68\x20\x20\x20\x20\x20\x20\x20\x20\x20\x54\x6f\x74\x61\x6c\x73\x3a\x20\x25\x31\x31\x2e\x31\x31\x73\x20\x25"
 		"\x31\x31\x2e\x31\x31\x73\x20\x20\x20\x20\x20\x25\x73\x0d\x0a" // 278 DownloadQueueTotals
 	,"\x0d\x0a\x01\x2d\x01\x67\x52\x65\x6d\x6f\x76\x65\x20\x77\x68\x69\x63\x68\x20\x66\x69\x6c\x65\x20\x66\x72\x6f\x6d\x20\x75\x70\x6c"
 		"\x6f\x61\x64\x20\x71\x75\x65\x75\x65\x20\x28\x31\x2d\x25\x64\x29\x3a\x20\x01\x6e" // 279 RemoveWhichFromUlQueue
 	,"\x0d\x0a\x01\x2d\x01\x67\x52\x65\x6d\x6f\x76\x65\x20\x77\x68\x69\x63\x68\x20\x66\x69\x6c\x65\x20\x66\x72\x6f\x6d\x20\x64\x6f\x77"
 		"\x6e\x6c\x6f\x61\x64\x20\x71\x75\x65\x75\x65\x20\x28\x31\x2d\x25\x64\x29\x3a\x20\x01\x6e" // 280 RemoveWhichFromDlQueue
-	,"\x0d\x0a\x55\x70\x6c\x6f\x61\x64\x20\x71\x75\x65\x75\x65\x20\x69\x73\x20\x65\x6d\x70\x74\x79\x0d\x0a" // 281 UploadQueueIsEmpty
+	,"\x0d\x0a\x55\x70\x6c\x6f\x61\x64\x20\x71\x75\x65\x75\x65\x20\x69\x73\x20\x65\x6d\x70\x74\x79\x2e\x0d\x0a" // 281 UploadQueueIsEmpty
 	,"\x0d\x0a\x48\x61\x6e\x67\x20\x75\x70\x20\x61\x66\x74\x65\x72\x20\x74\x72\x61\x6e\x73\x66\x65\x72" // 282 HangUpAfterXferQ
 	,"\x0d\x0a\x01\x77\x01\x68\x53\x74\x61\x72\x74\x20\x74\x72\x61\x6e\x73\x66\x65\x72\x20\x6e\x6f\x77\x20\x28\x43\x74\x72\x6c\x2d\x58"
 		"\x20\x74\x6f\x20\x61\x62\x6f\x72\x74\x29\x3a\x0d\x0a\x01\x6e" // 283 StartXferNow
-- 
GitLab


From dee21ed618ee1af36b63abf17ecdb89c1b5c793f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:23:23 -0700
Subject: [PATCH 595/752] Fix observed race-condition resulting in the HOSTNAME
 @-code being blank.

Eliminate the hack in each server where it will over-write startup host_name
(with the configured Internet email address), if it's blank. This hack was
subject to a race condition where the parent app (e.g. sbbsctrl.exe) would
clear or re-initialize the host_name after the sever threads had initialized.
Instead, just use a function which will return either the startup->host_name
or (fallback to) scfg.inet_addr.
---
 src/sbbs3/atcodes.cpp  |  2 +-
 src/sbbs3/bulkmail.cpp |  2 +-
 src/sbbs3/email.cpp    |  2 +-
 src/sbbs3/ftpsrvr.c    | 14 ++++++++------
 src/sbbs3/mailsrvr.c   | 36 +++++++++++++++++++-----------------
 src/sbbs3/main.cpp     |  7 ++-----
 src/sbbs3/netmail.cpp  |  8 ++++----
 src/sbbs3/postmsg.cpp  |  2 +-
 src/sbbs3/readmsgs.cpp |  2 +-
 src/sbbs3/sbbs.h       |  1 +
 src/sbbs3/services.c   | 10 ++++++----
 src/sbbs3/str.cpp      |  5 +++++
 src/sbbs3/websrvr.c    | 16 +++++++++-------
 src/sbbs3/writemsg.cpp |  2 +-
 14 files changed, 60 insertions(+), 49 deletions(-)

diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp
index 6b52a1ce6d..eb1625178d 100644
--- a/src/sbbs3/atcodes.cpp
+++ b/src/sbbs3/atcodes.cpp
@@ -505,7 +505,7 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool
 		return(cfg.sys_inetaddr);
 
 	if(!strcmp(sp,"HOSTNAME"))
-		return(startup->host_name);
+		return server_host_name();
 
 	if(!strcmp(sp,"FIDOADDR")) {
 		if(cfg.total_faddrs)
diff --git a/src/sbbs3/bulkmail.cpp b/src/sbbs3/bulkmail.cpp
index cbf1da28e1..8368a10430 100644
--- a/src/sbbs3/bulkmail.cpp
+++ b/src/sbbs3/bulkmail.cpp
@@ -117,7 +117,7 @@ bool sbbs_t::bulkmail(uchar *ar)
 
 	memset(&smb,0,sizeof(smb));
 	smb.subnum=INVALID_SUB;	/* mail database */
-	i=savemsg(&cfg, &smb, &msg, &client, startup->host_name, msgbuf, /* remsg: */NULL);
+	i=savemsg(&cfg, &smb, &msg, &client, server_host_name(), msgbuf, /* remsg: */NULL);
 	free(msgbuf);
 	if(i!=0) {
 		smb_close(&smb);
diff --git a/src/sbbs3/email.cpp b/src/sbbs3/email.cpp
index 53bfc6fa92..09de214750 100644
--- a/src/sbbs3/email.cpp
+++ b/src/sbbs3/email.cpp
@@ -303,7 +303,7 @@ bool sbbs_t::email(int usernumber, const char *top, const char *subj, long mode,
 
 	/* Security logging */
 	msg_client_hfields(&msg,&client);
-	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
+	smb_hfield_str(&msg,SENDERSERVER,server_host_name());
 
 	smb_hfield_str(&msg,SUBJECT,title);
 
diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index ecd5d1078f..b2dca8c89d 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -201,6 +201,11 @@ static BOOL winsock_startup(void)
 
 #endif
 
+static char* server_host_name(void)
+{
+	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
+}
+
 static void status(char* str)
 {
 	if(startup!=NULL && startup->status!=NULL)
@@ -593,7 +598,7 @@ js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, JSObject** ftp, js_c
 			break;
 
 		lprintf(LOG_DEBUG,"%04d JavaScript: Initializing System object",sock);
-		if(js_CreateSystemObject(js_cx, *glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) 
+		if(js_CreateSystemObject(js_cx, *glob, &scfg, uptime, server_host_name(), SOCKLIB_DESC)==NULL) 
 			break;
 
 		if((*ftp=JS_DefineObject(js_cx, *glob, "ftp", NULL
@@ -3124,7 +3129,7 @@ static void ctrl_thread(void* arg)
 		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
-	sockprintf(sock,sess,"220-%s (%s)",scfg.sys_name, startup->host_name);
+	sockprintf(sock,sess,"220-%s (%s)",scfg.sys_name, server_host_name());
 	sockprintf(sock,sess," Synchronet FTP Server %s-%s Ready"
 		,revision,PLATFORM_DESC);
 	sprintf(str,"%sftplogin.txt",scfg.text_dir);
@@ -3850,7 +3855,7 @@ static void ctrl_thread(void* arg)
 				ip_addr=0;
 				/* TODO: IPv6 this here lookup */
 				if(startup->options&FTP_OPT_LOOKUP_PASV_IP
-					&& (host=gethostbyname(startup->host_name))!=NULL) 
+					&& (host=gethostbyname(server_host_name()))!=NULL) 
 					ip_addr=ntohl(*((ulong*)host->h_addr_list[0]));
 				if(ip_addr==0 && (ip_addr=startup->pasv_ip_addr.s_addr)==0)
 					ip_addr=ntohl(pasv_addr.in.sin_addr.s_addr);
@@ -6107,9 +6112,6 @@ void DLLCALL ftp_server(void* arg)
 			break;
 		}
 
-		if(startup->host_name[0]==0)
-			SAFECOPY(startup->host_name,scfg.sys_inetaddr);
-
 		if((t=checktime())!=0) {   /* Check binary time */
 			lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
 		}
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index 32548b53e6..e7646f7536 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -237,6 +237,11 @@ static BOOL winsock_startup(void)
 
 #endif
 
+static char* server_host_name(void)
+{
+	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
+}
+
 static void update_clients(void)
 {
 	if(startup!=NULL && startup->clients!=NULL)
@@ -1143,7 +1148,7 @@ static void pop3_thread(void* arg)
 		srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId()));	/* seed random number generator */
 		rand();	/* throw-away first result */
 		safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>"
-			,rand(),socket,(ulong)time(NULL),(ulong)clock(),startup->host_name);
+			,rand(),socket,(ulong)time(NULL),(ulong)clock(), server_host_name());
 
 		sockprintf(socket,client.protocol,session,"+OK Synchronet %s Server %s-%s Ready %s"
 			,client.protocol, revision,PLATFORM_DESC,challenge);
@@ -2231,7 +2236,7 @@ js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailpr
 		if(*js_glob==NULL) {
 			/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
 			if(!js_CreateCommonObjects(*js_cx, &scfg, &scfg, NULL
-						,uptime, startup->host_name, SOCKLIB_DESC	/* system */
+						,uptime, server_host_name(), SOCKLIB_DESC	/* system */
 						,&js_callback								/* js */
 						,&startup->js
 						,client, sock, -1							/* client */
@@ -3181,7 +3186,7 @@ static void smtp_thread(void* arg)
 	/* SMTP session active: */
 
 	sockprintf(socket,client.protocol,session,"220 %s Synchronet %s Server %s-%s Ready"
-		,startup->host_name, client.protocol, revision, PLATFORM_DESC);
+		,server_host_name(), client.protocol, revision, PLATFORM_DESC);
 	while(1) {
 		rd = sockreadline(socket, client.protocol, session, buf, sizeof(buf));
 		if(rd<0) 
@@ -3712,7 +3717,7 @@ static void smtp_thread(void* arg)
 					smb_hfield_str(&msg, RECIPIENT, rcpt_name);
 
 					smb.subnum=subnum;
-					if((i=savemsg(&scfg, &smb, &msg, &client, startup->host_name, msgbuf, /* remsg: */NULL))!=SMB_SUCCESS) {
+					if((i=savemsg(&scfg, &smb, &msg, &client, server_host_name(), msgbuf, /* remsg: */NULL))!=SMB_SUCCESS) {
 						lprintf(LOG_WARNING,"%04d %s !ERROR %d (%s) posting message to %s (%s)"
 							,socket, client.protocol, i, smb.last_error, scfg.sub[subnum]->sname, smb.file);
 						sockprintf(socket,client.protocol,session, "452 ERROR %d (%s) posting message"
@@ -3818,7 +3823,7 @@ static void smtp_thread(void* arg)
 				/* E-mail */
 				smb.subnum=INVALID_SUB;
 				/* creates message data, but no header or index records (since msg.to==NULL) */
-				i=savemsg(&scfg, &smb, &msg, &client, startup->host_name, msgbuf, /* remsg: */NULL);
+				i=savemsg(&scfg, &smb, &msg, &client, server_host_name(), msgbuf, /* remsg: */NULL);
 				if(smb_countattachments(&smb, &msg, msgbuf) > 0)
 					msg.hdr.auxattr |= MSG_MIMEATTACH;
 				msg.hdr.netattr |= MSG_KILLSENT;
@@ -3877,7 +3882,7 @@ static void smtp_thread(void* arg)
 						,host_name,hello_name
 						,smtp.client_addr.addr.sa_family==AF_INET6?"IPv6: ":""
 						,host_ip
-						,startup->host_name
+						,server_host_name()
 						,server_addr.addr.sa_family==AF_INET6?"IPv6: ":""
 						,server_ip
 						,server_name
@@ -4068,7 +4073,7 @@ static void smtp_thread(void* arg)
 			p=buf+4;
 			SKIP_WHITESPACE(p);
 			SAFECOPY(hello_name,p);
-			sockprintf(socket,client.protocol,session,"250 %s",startup->host_name);
+			sockprintf(socket,client.protocol,session,"250 %s",server_host_name());
 			esmtp=FALSE;
 			state=SMTP_STATE_HELO;
 			cmd=SMTP_CMD_NONE;
@@ -4080,7 +4085,7 @@ static void smtp_thread(void* arg)
 			p=buf+4;
 			SKIP_WHITESPACE(p);
 			SAFECOPY(hello_name,p);
-			sockprintf(socket,client.protocol,session,"250-%s",startup->host_name);
+			sockprintf(socket,client.protocol,session,"250-%s",server_host_name());
 			sockprintf(socket,client.protocol,session,"250-AUTH PLAIN LOGIN CRAM-MD5");
 			sockprintf(socket,client.protocol,session,"250-SEND");
 			sockprintf(socket,client.protocol,session,"250-SOML");
@@ -4209,7 +4214,7 @@ static void smtp_thread(void* arg)
 		}
 		if(!stricmp(buf,"AUTH CRAM-MD5")) {
 			safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%s>"
-				,rand(),socket,(ulong)time(NULL),(ulong)clock(),startup->host_name);
+				,rand(),socket,(ulong)time(NULL),(ulong)clock(),server_host_name());
 #if 0
 			lprintf(LOG_DEBUG,"%04d SMTP CRAM-MD5 challenge: %s"
 				,socket,challenge);
@@ -4300,7 +4305,7 @@ static void smtp_thread(void* arg)
 			continue;
 		}
 		if(!stricmp(buf,"QUIT")) {
-			sockprintf(socket,client.protocol,session,"221 %s Service closing transmission channel",startup->host_name);
+			sockprintf(socket,client.protocol,session,"221 %s Service closing transmission channel",server_host_name());
 			break;
 		} 
 		if(!stricmp(buf,"NOOP")) {
@@ -5104,7 +5109,7 @@ BOOL bounce(SOCKET sock, smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate)
 	else
 		attempts[0]=0;
 	SAFEPRINTF2(str,"%s reporting delivery failure of message %s"
-		,startup->host_name, attempts);
+		,server_host_name(), attempts);
 	smb_hfield_str(&newmsg, SMB_COMMENT, str);
 	SAFEPRINTF2(str,"from %s to %s\r\n"
 		,msg->from
@@ -5294,7 +5299,7 @@ static SOCKET sendmail_negotiate(CRYPT_SESSION *session, smb_t *smb, smbmsg_t *m
 			return INVALID_SOCKET;
 		}
 
-		sockprintf(sock, "SEND", *session,"EHLO %s",startup->host_name);
+		sockprintf(sock, "SEND", *session,"EHLO %s",server_host_name());
 		switch (sockgetrsp_opt(sock, "SEND", *session,"250", "STARTTLS", buf, sizeof(buf))) {
 			case -1:
 				if(startup->options&MAIL_OPT_RELAY_TX 
@@ -5305,7 +5310,7 @@ static SOCKET sendmail_negotiate(CRYPT_SESSION *session, smb_t *smb, smbmsg_t *m
 					mail_close_socket(&sock, session);
 					return INVALID_SOCKET;
 				}
-				sockprintf(sock, "SEND", *session,"HELO %s",startup->host_name);
+				sockprintf(sock, "SEND", *session,"HELO %s",server_host_name());
 				if(!sockgetrsp(sock, "SEND", *session,"250",buf,sizeof(buf))) {
 					SAFEPRINTF3(err,badrsp_err,server,buf,"250");
 					remove_msg_intransit(smb,msg);
@@ -5371,7 +5376,7 @@ static SOCKET sendmail_negotiate(CRYPT_SESSION *session, smb_t *smb, smbmsg_t *m
 								continue;
 							}
 						}
-						sockprintf(sock,prot,*session,"EHLO %s",startup->host_name);
+						sockprintf(sock,prot,*session,"EHLO %s",server_host_name());
 						if(!sockgetrsp(sock, prot, *session,"250",buf,sizeof(buf))) {
 							SAFEPRINTF3(err,badrsp_err,server,buf,"220");
 							lprintf(LOG_INFO, "%04d SEND/TLS %s", sock, err);
@@ -6128,9 +6133,6 @@ void DLLCALL mail_server(void* arg)
 			iniCloseFile(fp);
 		}
 
-		if(startup->host_name[0]==0)
-			SAFECOPY(startup->host_name,scfg.sys_inetaddr);
-
 		if((t=checktime())!=0) {   /* Check binary time */
 			lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
 		}
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 7daab4f0bd..1556fea37b 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -1284,7 +1284,7 @@ JSContext* sbbs_t::js_init(JSRuntime** runtime, JSObject** glob, const char* des
 
 		/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
 		if(!js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
-					,uptime, startup->host_name, SOCKLIB_DESC	/* system */
+					,uptime, server_host_name(), SOCKLIB_DESC	/* system */
 					,&js_callback								/* js */
 					,&startup->js
 					,&client, client_socket, -1					/* client */
@@ -3077,7 +3077,7 @@ void event_thread(void* arg)
 					&& (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.event[i]->time
 				&& (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
 				&& sbbs->cfg.event[i]->days&(1<<now_tm.tm_wday)
-				&& (sbbs->cfg.event[i]->mdays==0
+				&& (sbbs->cfg.event[i]->mdays < 2
 					|| sbbs->cfg.event[i]->mdays&(1<<now_tm.tm_mday))
 				&& (sbbs->cfg.event[i]->months==0
 					|| sbbs->cfg.event[i]->months&(1<<now_tm.tm_mon))))
@@ -5095,9 +5095,6 @@ void DLLCALL bbs_thread(void* arg)
 		return;
 	}
 
-	if(startup->host_name[0]==0)
-		SAFECOPY(startup->host_name,scfg.sys_inetaddr);
-
 	if((t=checktime())!=0) {   /* Check binary time */
 		lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
 	}
diff --git a/src/sbbs3/netmail.cpp b/src/sbbs3/netmail.cpp
index 73cdfeeb87..80e9476171 100644
--- a/src/sbbs3/netmail.cpp
+++ b/src/sbbs3/netmail.cpp
@@ -311,7 +311,7 @@ bool sbbs_t::netmail(const char *into, const char *title, long mode, smb_t* resm
 	smb_t smb;
 	memset(&smb, 0, sizeof(smb));
 	smb.subnum = INVALID_SUB;
-	int result = savemsg(&cfg, &smb, &msg, &client, startup->host_name, buf, remsg);
+	int result = savemsg(&cfg, &smb, &msg, &client, server_host_name(), buf, remsg);
 	free(buf);
 	smb_close(&smb);
 	smb_freemsgmem(&msg);
@@ -1120,13 +1120,13 @@ bool sbbs_t::inetmail(const char *into, const char *subj, long mode, smb_t* resm
 
 	/* Security logging */
 	msg_client_hfields(&msg,&client);
-	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
+	smb_hfield_str(&msg,SENDERSERVER, server_host_name());
 
 	smb_hfield_str(&msg,SUBJECT,title);
 
 	editor_info_to_msg(&msg, editor, charset);
 
-	i = savemsg(&cfg, &smb, &msg, &client, startup->host_name, msgbuf, remsg);
+	i = savemsg(&cfg, &smb, &msg, &client, server_host_name(), msgbuf, remsg);
 	free(msgbuf);
 
 	if(i!=SMB_SUCCESS) {
@@ -1396,7 +1396,7 @@ bool sbbs_t::qnetmail(const char *into, const char *subj, long mode, smb_t* resm
 
 	/* Security logging */
 	msg_client_hfields(&msg,&client);
-	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
+	smb_hfield_str(&msg,SENDERSERVER, server_host_name());
 
 	smb_hfield_str(&msg,SUBJECT,title);
 
diff --git a/src/sbbs3/postmsg.cpp b/src/sbbs3/postmsg.cpp
index c1d30f6f60..c4899b75bc 100644
--- a/src/sbbs3/postmsg.cpp
+++ b/src/sbbs3/postmsg.cpp
@@ -297,7 +297,7 @@ bool sbbs_t::postmsg(uint subnum, long wm_mode, smb_t* resmb, smbmsg_t* remsg)
 
 	/* Security logging */
 	msg_client_hfields(&msg,&client);
-	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
+	smb_hfield_str(&msg,SENDERSERVER, server_host_name());
 
 	smb_hfield_str(&msg,SUBJECT,title);
 
diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp
index 726e062981..4fcf265ec2 100644
--- a/src/sbbs3/readmsgs.cpp
+++ b/src/sbbs3/readmsgs.cpp
@@ -1163,7 +1163,7 @@ int sbbs_t::scanposts(uint subnum, long mode, const char *find)
 
 				/* Security logging */
 				msg_client_hfields(&vote, &client);
-				smb_hfield_str(&vote, SENDERSERVER, startup->host_name);
+				smb_hfield_str(&vote, SENDERSERVER, server_host_name());
 
 				if((i=votemsg(&cfg, &smb, &vote, notice, text[VoteNoticeFmt])) != SMB_SUCCESS)
 					errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index d5cc3fcea3..dd2b758a10 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -643,6 +643,7 @@ public:
 	bool	gettimeleft_inside;
 
 	/* str.cpp */
+	char*	server_host_name(void);
 	char*	timestr(time_t);
 	char*	datestr(time_t);
     char	timestr_output[60];
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index 9c3019e020..f7ed012847 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -182,6 +182,11 @@ static BOOL winsock_startup(void)
 
 #endif
 
+static char* server_host_name(void)
+{
+	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
+}
+
 static ulong active_clients(void)
 {
 	ulong i;
@@ -824,7 +829,7 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client,
 		if(!js_CreateUserObjects(js_cx, *glob, &scfg, /*user: */NULL, service_client->client, NULL, service_client->subscan)) 
 			break;
 
-		if(js_CreateSystemObject(js_cx, *glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) 
+		if(js_CreateSystemObject(js_cx, *glob, &scfg, uptime, server_host_name(), SOCKLIB_DESC)==NULL) 
 			break;
 
 		if(service_client->service->js_server_props.version[0]==0) {
@@ -1840,9 +1845,6 @@ void DLLCALL services_thread(void* arg)
 			return;
 		}
 
-		if(startup->host_name[0]==0)
-			SAFECOPY(startup->host_name,scfg.sys_inetaddr);
-
 		if((t=checktime())!=0) {   /* Check binary time */
 			lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
 		}
diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp
index 70071f0358..3200e3f67f 100644
--- a/src/sbbs3/str.cpp
+++ b/src/sbbs3/str.cpp
@@ -1272,3 +1272,8 @@ char* sbbs_t::age_of_posted_item(char* buf, size_t max, time_t t)
 	safe_snprintf(buf, max, text[AgeOfPostedItem], value, units, past);
 	return buf;
 }
+
+char* sbbs_t::server_host_name(void)
+{
+	return startup->host_name[0] ? startup->host_name : cfg.sys_inetaddr;
+}
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 762e31f9bc..53619e75de 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -740,6 +740,11 @@ static BOOL winsock_startup(void)
 
 #endif
 
+static char* server_host_name(void)
+{
+	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
+}
+
 static void status(char* str)
 {
 	if(startup!=NULL && startup->status!=NULL)
@@ -2797,7 +2802,7 @@ static BOOL parse_headers(http_session_t * session)
 	}
 	if(content_len)
 		session->req.post_len = content_len;
-	add_env(session,"SERVER_NAME",session->req.host[0] ? session->req.host : startup->host_name );
+	add_env(session,"SERVER_NAME",session->req.host[0] ? session->req.host : server_host_name() );
 	return TRUE;
 }
 
@@ -3125,12 +3130,12 @@ static BOOL get_request_headers(http_session_t * session)
 	}
 
 	if(!(session->req.vhost[0])) {
-		SAFECOPY(session->req.vhost, startup->host_name);
+		SAFECOPY(session->req.vhost, server_host_name());
 		/* Lower-case for normalization */
 		strlwr(session->req.vhost);
 	}
 	if(!(session->req.host[0])) {
-		SAFECOPY(session->req.host, startup->host_name);
+		SAFECOPY(session->req.host, server_host_name());
 		/* Lower-case for normalization */
 		strlwr(session->req.host);
 	}
@@ -5764,7 +5769,7 @@ js_initcx(http_session_t *session)
 	if(!js_CreateCommonObjects(js_cx, &scfg, NULL
 									,NULL						/* global */
 									,uptime						/* system */
-									,startup->host_name			/* system */
+									,server_host_name()			/* system */
 									,SOCKLIB_DESC				/* system */
 									,&session->js_callback		/* js */
 									,&startup->js				/* js */
@@ -7067,9 +7072,6 @@ void DLLCALL web_server(void* arg)
 			iniCloseFile(fp);
 		}
 
-		if(startup->host_name[0]==0)
-			SAFECOPY(startup->host_name,scfg.sys_inetaddr);
-
 		if(uptime==0)
 			uptime=time(NULL);	/* this must be done *after* setting the timezone */
 
diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp
index 6387eefa8b..c8fcda6fff 100644
--- a/src/sbbs3/writemsg.cpp
+++ b/src/sbbs3/writemsg.cpp
@@ -1378,7 +1378,7 @@ void sbbs_t::forwardmail(smbmsg_t *msg, int usernumber)
 
 	/* Security logging */
 	msg_client_hfields(msg,&client);
-	smb_hfield_str(msg,SENDERSERVER,startup->host_name);
+	smb_hfield_str(msg,SENDERSERVER, server_host_name());
 
 	username(&cfg,usernumber,touser);
 	smb_hfield_str(msg,RECIPIENT,touser);
-- 
GitLab


From 5dcfe61b5864d8fbdfb69429c302bfec300704bd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:31:10 -0700
Subject: [PATCH 596/752] Fix typos.

---
 xtrn/gooble/install-xtrn.ini | 2 +-
 xtrn/tw2/install-xtrn.ini    | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/xtrn/gooble/install-xtrn.ini b/xtrn/gooble/install-xtrn.ini
index f5849f4fdb..2a3cdad325 100644
--- a/xtrn/gooble/install-xtrn.ini
+++ b/xtrn/gooble/install-xtrn.ini
@@ -12,6 +12,6 @@ required = true
 
 !include install-json-service.ini
 
-[ini:json-service.ini:gooble]
+[ini:json-service.ini:gooble2]
 keys=dir
 values=startup_dir
\ No newline at end of file
diff --git a/xtrn/tw2/install-xtrn.ini b/xtrn/tw2/install-xtrn.ini
index 350ed7c045..c9613009b7 100644
--- a/xtrn/tw2/install-xtrn.ini
+++ b/xtrn/tw2/install-xtrn.ini
@@ -6,7 +6,7 @@ Subs: Space, War, Trade, Classic, Multiplayer, JavaScript, Port
 Inst: $Id: install-xtrn.ini,v 1.2 2020/04/21 03:25:59 rswindell Exp $
 
 [prog:TW2]
-cmd  = ?tw.js
+cmd  = ?tw2.js
 settings = XTRN_MULTIUSER
 required = true
 
@@ -17,4 +17,4 @@ keys=dir
 values=startup_dir
 
 [exec:twint500.js]
-prompt=Configure and Initialize TW2?
\ No newline at end of file
+prompt=Configure and Initialize TW2
\ No newline at end of file
-- 
GitLab


From f8a64508ec0ac19959a2bee984f1541b3b83d869 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:33:17 -0700
Subject: [PATCH 597/752] Install and trigger (via semfile) the TBDROLL event.

---
 xtrn/tbd/install-xtrn.ini | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/xtrn/tbd/install-xtrn.ini b/xtrn/tbd/install-xtrn.ini
index 8271356f9e..a65b2b107e 100644
--- a/xtrn/tbd/install-xtrn.ini
+++ b/xtrn/tbd/install-xtrn.ini
@@ -2,7 +2,7 @@ Name: The Beast's Domain
 By:   King Drafus (Allen Christiansen), Domain Entertainment
 Cats: Games
 Subs: Adventure, Native
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:41:51 rswindell Exp $
+Inst: 2020/09/06
 
 [prog:TBD]
 cmd  = tbd%. /times=10
@@ -10,3 +10,14 @@ settings = XTRN_MULTIUSER|XTRN_NATIVE|XTRN_MODUSERDAT
 execution_ars = ANSI
 type = XTRN_SBBS
 required = true
+
+[event:TBDROLL]
+prompt = false
+name = TBD Re-roll
+cmd = reroll%.
+settings = XTRN_NATIVE
+node_num = 1
+days = 127
+
+[eval:file_touch(system.data_dir + 'tbdroll.now')]
+prompt = false
-- 
GitLab


From 29731fb321d935053a38c4da9190e6959b5785dd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:39:23 -0700
Subject: [PATCH 598/752] Add install option for his IGM.

---
 xtrn/lord/pickle/pickle.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/xtrn/lord/pickle/pickle.js b/xtrn/lord/pickle/pickle.js
index bee5ba2e59..e806a47178 100644
--- a/xtrn/lord/pickle/pickle.js
+++ b/xtrn/lord/pickle/pickle.js
@@ -141,6 +141,12 @@ function menu() {
 		exit(0);
 }
 
+if (argc == 1 && argv[0] == 'INSTALL') {
+        var install = {
+                desc:'`0T`2ravel `0T`2o Other Places Pickles',
+        }
+        exit(0);
+}
 // possible record changes: hp, hp_max, forest_fights, gold, bank, def, str, cha, gem, des1
 
 menu();
-- 
GitLab


From 17b0e847aade93fd3976c1aab9e877555906d923 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 6 Sep 2020 22:40:13 -0700
Subject: [PATCH 599/752] Updated for v3.18b.

---
 docs/install.rtf | Bin 5644 -> 65452 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/docs/install.rtf b/docs/install.rtf
index a395399e109f0fbbd348a576c7f1cbebdedaae18..69e8411d02b8f3940dd86856c0e48382683f255e 100644
GIT binary patch
literal 65452
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

literal 5644
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
Pz|@8<G{A1H<zfH;WMA>8

-- 
GitLab


From ecddfe462ee6655e08f3c3994107bdd471bc0a2f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 00:09:42 -0700
Subject: [PATCH 600/752] Preparing for the v3.18b release (for Windows).

---
 install/install.iss | 69 +++++++++++++++++++++++----------------------
 1 file changed, 36 insertions(+), 33 deletions(-)

diff --git a/install/install.iss b/install/install.iss
index bb196b6842..624504dab4 100644
--- a/install/install.iss
+++ b/install/install.iss
@@ -2,7 +2,7 @@
 ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
 
 #define MyAppName "Synchronet BBS Software"
-#define MyAppVersion "3.17b"
+#define MyAppVersion "3.18b"
 #define MyAppPublisher "Rob Swindell"
 #define MyAppURL "http://www.synchro.net/"
 #define MyAppSupportURL "http://wiki.synchro.net/howto:support"
@@ -10,7 +10,7 @@
 #define MyAppExeName "sbbsctrl.exe"
 #define sbbsCtrlPanel "Synchronet Control Panel"
 #define release "release"
-#define source "e:\src\sbbs317"
+#define source "e:\sbbs"
 
 [Setup]
 ; NOTE: The value of AppId uniquely identifies this application.
@@ -24,8 +24,8 @@ AppPublisher={#MyAppPublisher}
 AppPublisherURL={#MyAppURL}
 AppSupportURL={#MyAppSupportURL}
 AppUpdatesURL={#MyAppDownloadURL}
-AppCopyright=Copyright 2019 {#MyAppPublisher}
-VersionInfoVersion=3.17.1
+AppCopyright=Copyright 2020 {#MyAppPublisher}
+VersionInfoVersion=3.18.1
 AppMutex=sbbs_running,sbbsctrl_running
 DefaultDirName={sd}\sbbs
 DefaultGroupName={#MyAppName}
@@ -36,7 +36,7 @@ OutputBaseFilename=setup
 Compression=lzma
 SolidCompression=yes
 MinVersion = 5.1
-InfoBeforeFile=e:\stock\docs\install.rtf
+InfoBeforeFile=docs\install.rtf
 ChangesEnvironment=yes
 SourceDir={#source}
 UninstallDisplayIcon="{app}\exec\sbbsctrl.exe"
@@ -62,34 +62,37 @@ name: "web_runemaster"; Description: "Enable Legacy/Runemaster Web Interface"; G
 name: "web_echicken"; Description: "Enable echicken's Web Interface (v4)"; GroupDescription: {#webui_group}; Flags: exclusive unchecked
 
 [Files]
-Source: "src\sbbs3\ctrl\sbbsctrl.exe";                         DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "src\sbbs3\chat\chat.exe";                             DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "src\sbbs3\useredit\useredit.exe";                     DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "src\sbbs3\msvc.win32.exe.{#release}\*.exe";           DestDir: "{app}\exec";  Flags: ignoreversion; Excludes: "textgen.exe, v4upgrade.exe"
-Source: "src\sbbs3\msvc.win32.dll.{#release}\*.dll";           DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "src\sbbs3\msvc.win32.dll.release\sbbsexec.dll";       DestDir: "{sys}";
-Source: "src\sbbs3\scfg\msvc.win32.exe.{#release}\scfg.exe";   DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "3rdp\win32.release\nspr\bin\nspr4.dll";       	       DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "3rdp\win32.release\mozjs\bin\mozjs185-1.0.dll";       DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "3rdp\win32.release\cryptlib\bin\cl32.dll";            DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "src\sexpots\{#release}\sexpots.exe";                     DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "s:\sbbs\exec\user.com";                                DestDir: "{app}\exec";  Flags: ignoreversion; Check: not IsWin64
-Source: "s:\sbbs\exec\mlabels.exe";                             DestDir: "{app}\exec";  Flags: ignoreversion; Check: not IsWin64
-Source: "s:\sbbs\exec\svdmansi.com";                            DestDir: "{app}\exec";  Flags: ignoreversion; Check: not IsWin64
-Source: "s:\sbbs\exec\dosxtrn.exe";                             DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "e:\stock\node1\node.cnf";                                DestDir: "{app}\node1"; Flags: ignoreversion
-Source: "e:\stock\node1\node.cnf";                                DestDir: "{app}\node2"; Flags: ignoreversion
-Source: "e:\stock\node1\node.cnf";                                DestDir: "{app}\node3"; Flags: ignoreversion
-Source: "e:\stock\node1\node.cnf";                                DestDir: "{app}\node4"; Flags: ignoreversion
-Source: "e:\stock\exec\*";   DestDir: "{app}\exec";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*,baja.js,menu.js,sbbsedit.js,jsdocs.js,testbuild.js,load\menulib.js"
-Source: "e:\stock\ctrl\*";   DestDir: "{app}\ctrl";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*,*.?.cnf,*.?.ini"
-Source: "e:\stock\text\*";   DestDir: "{app}\text";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*"
-Source: "e:\stock\xtrn\*";   DestDir: "{app}\xtrn";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*,server.ini"
-Source: "e:\stock\docs\*";   DestDir: "{app}\docs";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*"
-Source: "e:\stock\web\*";    DestDir: "{app}\web";   Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*"
-Source: "e:\stock\webv4\*";  DestDir: "{app}\webv4"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "CVS,.#*"
-Source: "c:\bin\zip.exe"; DestDir: "{app}\exec";  Flags: ignoreversion
-Source: "c:\bin\unzip.exe"; DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "src\sbbs3\ctrl\sbbsctrl.exe";                        DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "src\sbbs3\chat\chat.exe";                            DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "src\sbbs3\useredit\useredit.exe";                    DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "src\sbbs3\msvc.win32.exe.{#release}\*.exe";          DestDir: "{app}\exec";  Flags: ignoreversion; Excludes: "textgen.exe, v4upgrade.exe"
+Source: "src\sbbs3\msvc.win32.dll.{#release}\*.dll";          DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "s:\sbbs\exec\sbbsexec.dll";                          DestDir: "{sys}";
+Source: "src\sbbs3\scfg\msvc.win32.exe.{#release}\scfg.exe";  DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "3rdp\win32.release\nspr\bin\nspr4.dll";       	      DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "3rdp\win32.release\mozjs\bin\mozjs185-1.0.dll";      DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "3rdp\win32.release\cryptlib\bin\cl32.dll";           DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "src\sexpots\{#release}\sexpots.exe";                 DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "s:\sbbs\exec\user.com";                              DestDir: "{app}\exec";  Flags: ignoreversion; Check: not IsWin64
+Source: "s:\sbbs\exec\mlabels.exe";                           DestDir: "{app}\exec";  Flags: ignoreversion; Check: not IsWin64
+Source: "s:\sbbs\exec\svdmansi.com";                          DestDir: "{app}\exec";  Flags: ignoreversion; Check: not IsWin64
+Source: "s:\sbbs\exec\dosxtrn.exe";                           DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "node1\node.cnf";                                     DestDir: "{app}\node1"; Flags: ignoreversion
+Source: "node1\node.cnf";                                     DestDir: "{app}\node2"; Flags: ignoreversion
+Source: "node1\node.cnf";                                     DestDir: "{app}\node3"; Flags: ignoreversion
+Source: "node1\node.cnf";                                     DestDir: "{app}\node4"; Flags: ignoreversion
+Source: "exec\*";   DestDir: "{app}\exec";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "baja.js,menu.js,sbbsedit.js,jsdocs.js,testbuild.js,load\menulib.js"
+Source: "ctrl\*";   DestDir: "{app}\ctrl";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.?.cnf,*.?.ini"
+Source: "text\*";   DestDir: "{app}\text";  Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "xtrn\*";   DestDir: "{app}\xtrn";  Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "docs\*";   DestDir: "{app}\docs";  Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "web\*";    DestDir: "{app}\web";   Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "e:\webv4\web\*";                                     DestDir: "{app}\webv4";            Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "e:\webv4\web\pages\.examples\*";                     DestDir: "{app}\webv4\pages";      Flags: ignoreversion recursesubdirs
+Source: "e:\webv4\web\components\.examples\*";                DestDir: "{app}\webv4\components"; Flags: ignoreversion recursesubdirs
+Source: "e:\webv4\web\sidebar\.examples\*";                   DestDir: "{app}\webv4\sidebar";    Flags: ignoreversion recursesubdirs
+Source: "c:\bin\zip.exe";                                     DestDir: "{app}\exec";  Flags: ignoreversion
+Source: "c:\bin\unzip.exe";                                   DestDir: "{app}\exec";  Flags: ignoreversion
 ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
 
 [Icons]
-- 
GitLab


From f79914b99af67e642e1a4609649f1e08a2070a52 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 00:17:22 -0700
Subject: [PATCH 601/752] Only install one default game, xtrn-setup can be used
 for the others.

I would have installed no games by default, but lbshell currently locks up
when selecting an external program section that has no available programs
within it. https://gitlab.synchro.net/sbbs/sbbs/-/issues/169
---
 ctrl/file.cnf | Bin 12424 -> 12424 bytes
 ctrl/main.cnf | Bin 6633 -> 6633 bytes
 ctrl/xtrn.cnf | Bin 14210 -> 8578 bytes
 3 files changed, 0 insertions(+), 0 deletions(-)

diff --git a/ctrl/file.cnf b/ctrl/file.cnf
index f8c9922e30d9c9f20ae71bc5e0484563c395598e..3c9957aa695aea170eb2c4624a4426fd8aafeeb7 100644
GIT binary patch
delta 571
zcmeB3>`2_8E~NOM0N|ZmC?q>kfq!#}&<{oh0*e1{?h#RDBCP7X7%{4ZB$b)82-(QM
z5L%L%lUb6PI(Z_C;^qcvB3wE7psdV91^&siWTiIWmAyxVw{FW5<6t=@VpJun5Tj~|
Z8ZoM#YY?MKOPd%~xw=HCn!G{(1^_LV<qrS=

delta 162
zcmeB3>`2_8E;La=fsuD|p^)rE1^&$?LO&QMDkyF46H#OW(HF(Q^kiE}zRgOK!XUB9
z4bm(unI)O2lP9t$Zf=kUYnXgkRtlv5uq=ejrU2%$DS)|~6_g;x^r&!b&Q_5G8MAqr
Y8dz-eOAQFEqYa_+bwRYk<PG{a02x#=g#Z8m

diff --git a/ctrl/main.cnf b/ctrl/main.cnf
index fbd14b09d5d5439d5b4559c619436eadc9f9320a..4188c7f9f9f3288ea768d8751a5ab86606c63e0e 100644
GIT binary patch
delta 96
zcmaE9{L*;CQ&!%@(vtk#;&cXvijtzd$q!i7Ir8#TQgbqkOC}pKSqQ*53c88OC7ETZ
p6F(Yo6%=KbC6=V-7N<`(WHRH-$xqMEgK99JEXnp~vna<EK>!a}BrE^`

delta 28
kcmaE9{L*;CQ`X5wOeT{h+5b#7V1GK<fL(gCD905+0Iq-v#sB~S

diff --git a/ctrl/xtrn.cnf b/ctrl/xtrn.cnf
index 854d84672a54037c7289437053df6c1a1c3e445b..ab020fc8a102b0e658d0d5d29556f36142c81bd9 100644
GIT binary patch
delta 230
zcmZq5Z*tyng@ciM^Hq*y#?3Rh_c3jrrl`!ws6V-o%VzR6)lNo%%?@g^EQ}13<FwvS
zzMv{7z`(%Z=IP?^?B?#s;OH0ZIk{29pP$76B+kIVpsJXfoRL4-Q|;7bGi~w72I_*`
z2n{Zi8%07ySR5E20HQWIEnQV_@_Fr}o2TjgVPs^UJXvocBlBcaeGqvaOja6z*wTg|
jaxa*SHv+L4jX~sMFzIRnVm}9you(kRwi$>#Yqk{to=HOr

delta 1488
zcmZp2Zpz<qg@aLg^Hq*yMt#TJ#FEs~A_WZ(M_)~apv08Sd<Fl4)S|?a{33;r)Z&t2
z1_<zQ^bK(ho_vwbd7^^w<h@*6>=}u<C8@<FlN01jCpU09^XuuwRFo9u#lXY`Ih{9K
zaP4C<W?*pi3-(mdS8&S7PtI0wEGkiOPt3_lEvkeVjYNVJhIzV%PZZQ*0kJ0vs)6j{
zNX#qFEXzzSpIpdUE}*BU53)!fE-1+9#1UMXmmHp$lQU6JkvBNf&pF)D#|Lch<U-B}
zPW$4@yyWu4oE)fYodjU6g-Zx>I_NVp_@t(%=A|h3rzwPFq$&iZrYN`+C8p=+A-NI4
z^zjdJnJB2o;pZQs;2sJN86}WYS#t7=QlPfD@xYu478K-k;b&xUDosktNmX#lPfyQD
zohT^7>lEtb<K*uScdb9j7`7y^Jk%f;xSx|slX8;s({m;ZaykexGB{^sCTFMSDY&HO
zWR|5CRZbL?<aPFlaB=nV41@U|Y#x6yL}dz8A=qFAPHS<P3(@5TIW4&v8C)`xQx(D!
zi>f9HD)72^I=hBD21TJ5%#xCsoI1IXvl{M1kU)82Q5DFET#O7ZMWyLJ`9&$9h;a!D
zbq56;iU}MkMWyNB078yjxS$}X0~aHMTVjcVTV`<v$P_on5I4_Y4@k0_T*wLbAbVP3
zNm^!c#^gfI3VxWUU}A!tj^d0A-idj6`FX_(iFqjs-ialN1*JJ9#S;ZZn7#eHCl_)?
zg2J6SJ1-j?HVvHa+%RK6VuGBm9H8Wpo1a%aQBaxJ$JN*0FPH(<5o|fBAbF?(E_^Tp
zpu&QjPTY(P!6k`B3L!<Q*`P2D4si?$33ByD4iRqllEk8tqSS1Z)Lsk|6y$UOnNpdT
zU6h}cm<Kk+-`n5G(GSHj+#E0!lM6Y^1Yu4ohKmYv+Ve3ogk)qE6_-?&W#)iVqbzTT
zhi6c5NF<6;T<j$fRVaZA6BFcg<Y8n8O-d~SW$+5ciGuRHp-!$rPCot-$ll^&Ny^Eu
zKuId4NvTC(K|xN3&EI&G87EKTwVnKpM?zX5JTosZwFp-FfbyJgaJZ+RpKH+MLe3Cb
zMFs{2Mv$m|Zf0I;ad~QLL28jgIYiUsLe8AYY<$8X(?l2<Kq*ea)6+`9FST4DJijO>
zWul-sD@fF6av^6pDB4*;=?}!K63{PBN-BmFFc3jOPKU{1e4Uf+1%xJ-h#cCyncs_<
zk#X`rq4yI7McCauUHqNh+$R@uh6=GdfLz4Dz@Vy_nw*iJoR&WMqUh1hQ^fu;PE_ES
zJWFC0h_aEK#mF%Ey5vF-r$GwDk(OS_$S`>?n2eVJOK`}76|a&7$-Byd<e!7dPI(Yp
LTLDC#RoDsuE!M@w

-- 
GitLab


From 3f16cac564104418163afe1cf752ab6906a78c63 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 00:25:26 -0700
Subject: [PATCH 602/752] Preparing for merge with sbbs repo.

---
 .gitignore                                    |  14 -
 README.md                                     |   4 -
 mods/js-date-format.js                        | 290 ------------------
 mods/webv4-installer.js                       | 276 -----------------
 text/synch.ans                                |  21 --
 .../components/.examples/footer.xjs           |   0
 .../components/.examples/header.xjs           |   0
 {web => webv4}/components/.examples/modal.xjs |   0
 .../components/.examples/navbar.xjs           |   0
 {web => webv4}/lib/auth.js                    |   0
 {web => webv4}/lib/avatars.js                 |   0
 {web => webv4}/lib/events/forum.js            |   0
 {web => webv4}/lib/events/mail.js             |   0
 {web => webv4}/lib/events/nodelist.js         |   0
 {web => webv4}/lib/events/sbbsimsg.js         |   0
 {web => webv4}/lib/events/telegram.js         |   0
 {web => webv4}/lib/files.js                   |   0
 {web => webv4}/lib/forum.js                   |   0
 {web => webv4}/lib/ftelnet.js                 |   0
 {web => webv4}/lib/init.js                    |   0
 {web => webv4}/lib/locale.js                  |   0
 {web => webv4}/lib/locale/en_ca.ini           |   0
 {web => webv4}/lib/locale/en_ca.js            |   0
 {web => webv4}/lib/locale/en_us.ini           |   0
 {web => webv4}/lib/locale/en_us.js            |   0
 {web => webv4}/lib/locale/es_ar.ini           |   0
 {web => webv4}/lib/mime-decode.js             |   0
 {web => webv4}/lib/pages.js                   |   0
 {web => webv4}/lib/request.js                 |   0
 {web => webv4}/lib/sidebar.js                 |   0
 {web => webv4}/pages/.examples/000-home.xjs   |   0
 {web => webv4}/pages/.examples/000-mail.xjs   |   0
 .../pages/.examples/000-register.xjs          |   0
 {web => webv4}/pages/.examples/001-forum.ssjs |   0
 {web => webv4}/pages/.examples/001-forum.xjs  |   0
 {web => webv4}/pages/.examples/002-files.xjs  |   0
 {web => webv4}/pages/.examples/003-games.xjs  |   0
 .../pages/.examples/More/001-userlist.xjs     |   0
 .../pages/.examples/More/999-sbbslist.xjs     |   0
 .../pages/.examples/More/webctrl.ini          |   0
 {web => webv4}/pages/.examples/webctrl.ini    |   0
 {web => webv4}/root/api/attachments.ssjs      |   0
 {web => webv4}/root/api/auth.ssjs             |   0
 {web => webv4}/root/api/events.ssjs           |   0
 {web => webv4}/root/api/files.ssjs            |   0
 {web => webv4}/root/api/forum.ssjs            |   0
 {web => webv4}/root/api/github.ssjs           |   0
 {web => webv4}/root/api/register.ssjs         |   0
 {web => webv4}/root/api/sbbsimsg.ssjs         |   0
 {web => webv4}/root/api/system.ssjs           |   0
 .../root/bootstrap/css/bootstrap-theme.css    |   0
 .../bootstrap/css/bootstrap-theme.css.map     |   0
 .../bootstrap/css/bootstrap-theme.min.css     |   0
 .../root/bootstrap/css/bootstrap.css          |   0
 .../root/bootstrap/css/bootstrap.css.map      |   0
 .../root/bootstrap/css/bootstrap.min.css      |   0
 .../fonts/glyphicons-halflings-regular.eot    | Bin
 .../fonts/glyphicons-halflings-regular.svg    |   0
 .../fonts/glyphicons-halflings-regular.ttf    | Bin
 .../fonts/glyphicons-halflings-regular.woff   | Bin
 .../fonts/glyphicons-halflings-regular.woff2  | Bin
 {web => webv4}/root/bootstrap/js/bootstrap.js |   0
 .../root/bootstrap/js/bootstrap.min.js        |   0
 {web => webv4}/root/bootstrap/js/npm.js       |   0
 {web => webv4}/root/css/navbar-fixed-top.css  |   0
 {web => webv4}/root/css/offcanvas.css         |   0
 {web => webv4}/root/css/style.css             |   0
 {web => webv4}/root/error/400.html            |   0
 {web => webv4}/root/error/401.html            |   0
 {web => webv4}/root/error/403.html            |   0
 {web => webv4}/root/error/404.html            |   0
 {web => webv4}/root/error/414.html            |   0
 {web => webv4}/root/error/416.html            |   0
 {web => webv4}/root/error/500.html            |   0
 {web => webv4}/root/error/501.html            |   0
 .../root/images/ajax-loader-small.gif         | Bin
 {web => webv4}/root/images/cp437-ibm-vga8.png | Bin
 {web => webv4}/root/images/favicon.ico        | Bin
 {web => webv4}/root/index.xjs                 |   0
 {web => webv4}/root/js/avatars.js             |   0
 {web => webv4}/root/js/common.js              |   0
 {web => webv4}/root/js/forum.js               |   0
 {web => webv4}/root/js/graphics-converter.js  |   0
 {web => webv4}/root/js/jquery.min.js          |   0
 {web => webv4}/root/js/offcanvas.js           |   0
 {web => webv4}/root/js/validator.js           |   0
 .../sidebar/.examples/001-nodelist.xjs        |   0
 .../sidebar/.examples/002-recent-visitors.xjs |   0
 .../sidebar/.examples/003-systemStats.xjs     |   0
 89 files changed, 605 deletions(-)
 delete mode 100644 .gitignore
 delete mode 100644 README.md
 delete mode 100644 mods/js-date-format.js
 delete mode 100644 mods/webv4-installer.js
 delete mode 100644 text/synch.ans
 rename {web => webv4}/components/.examples/footer.xjs (100%)
 rename {web => webv4}/components/.examples/header.xjs (100%)
 rename {web => webv4}/components/.examples/modal.xjs (100%)
 rename {web => webv4}/components/.examples/navbar.xjs (100%)
 rename {web => webv4}/lib/auth.js (100%)
 rename {web => webv4}/lib/avatars.js (100%)
 rename {web => webv4}/lib/events/forum.js (100%)
 rename {web => webv4}/lib/events/mail.js (100%)
 rename {web => webv4}/lib/events/nodelist.js (100%)
 rename {web => webv4}/lib/events/sbbsimsg.js (100%)
 rename {web => webv4}/lib/events/telegram.js (100%)
 rename {web => webv4}/lib/files.js (100%)
 rename {web => webv4}/lib/forum.js (100%)
 rename {web => webv4}/lib/ftelnet.js (100%)
 rename {web => webv4}/lib/init.js (100%)
 rename {web => webv4}/lib/locale.js (100%)
 rename {web => webv4}/lib/locale/en_ca.ini (100%)
 rename {web => webv4}/lib/locale/en_ca.js (100%)
 rename {web => webv4}/lib/locale/en_us.ini (100%)
 rename {web => webv4}/lib/locale/en_us.js (100%)
 rename {web => webv4}/lib/locale/es_ar.ini (100%)
 rename {web => webv4}/lib/mime-decode.js (100%)
 rename {web => webv4}/lib/pages.js (100%)
 rename {web => webv4}/lib/request.js (100%)
 rename {web => webv4}/lib/sidebar.js (100%)
 rename {web => webv4}/pages/.examples/000-home.xjs (100%)
 rename {web => webv4}/pages/.examples/000-mail.xjs (100%)
 rename {web => webv4}/pages/.examples/000-register.xjs (100%)
 rename {web => webv4}/pages/.examples/001-forum.ssjs (100%)
 rename {web => webv4}/pages/.examples/001-forum.xjs (100%)
 rename {web => webv4}/pages/.examples/002-files.xjs (100%)
 rename {web => webv4}/pages/.examples/003-games.xjs (100%)
 rename {web => webv4}/pages/.examples/More/001-userlist.xjs (100%)
 rename {web => webv4}/pages/.examples/More/999-sbbslist.xjs (100%)
 rename {web => webv4}/pages/.examples/More/webctrl.ini (100%)
 rename {web => webv4}/pages/.examples/webctrl.ini (100%)
 rename {web => webv4}/root/api/attachments.ssjs (100%)
 rename {web => webv4}/root/api/auth.ssjs (100%)
 rename {web => webv4}/root/api/events.ssjs (100%)
 rename {web => webv4}/root/api/files.ssjs (100%)
 rename {web => webv4}/root/api/forum.ssjs (100%)
 rename {web => webv4}/root/api/github.ssjs (100%)
 rename {web => webv4}/root/api/register.ssjs (100%)
 rename {web => webv4}/root/api/sbbsimsg.ssjs (100%)
 rename {web => webv4}/root/api/system.ssjs (100%)
 rename {web => webv4}/root/bootstrap/css/bootstrap-theme.css (100%)
 rename {web => webv4}/root/bootstrap/css/bootstrap-theme.css.map (100%)
 rename {web => webv4}/root/bootstrap/css/bootstrap-theme.min.css (100%)
 rename {web => webv4}/root/bootstrap/css/bootstrap.css (100%)
 rename {web => webv4}/root/bootstrap/css/bootstrap.css.map (100%)
 rename {web => webv4}/root/bootstrap/css/bootstrap.min.css (100%)
 rename {web => webv4}/root/bootstrap/fonts/glyphicons-halflings-regular.eot (100%)
 rename {web => webv4}/root/bootstrap/fonts/glyphicons-halflings-regular.svg (100%)
 rename {web => webv4}/root/bootstrap/fonts/glyphicons-halflings-regular.ttf (100%)
 rename {web => webv4}/root/bootstrap/fonts/glyphicons-halflings-regular.woff (100%)
 rename {web => webv4}/root/bootstrap/fonts/glyphicons-halflings-regular.woff2 (100%)
 rename {web => webv4}/root/bootstrap/js/bootstrap.js (100%)
 rename {web => webv4}/root/bootstrap/js/bootstrap.min.js (100%)
 rename {web => webv4}/root/bootstrap/js/npm.js (100%)
 rename {web => webv4}/root/css/navbar-fixed-top.css (100%)
 rename {web => webv4}/root/css/offcanvas.css (100%)
 rename {web => webv4}/root/css/style.css (100%)
 rename {web => webv4}/root/error/400.html (100%)
 rename {web => webv4}/root/error/401.html (100%)
 rename {web => webv4}/root/error/403.html (100%)
 rename {web => webv4}/root/error/404.html (100%)
 rename {web => webv4}/root/error/414.html (100%)
 rename {web => webv4}/root/error/416.html (100%)
 rename {web => webv4}/root/error/500.html (100%)
 rename {web => webv4}/root/error/501.html (100%)
 rename {web => webv4}/root/images/ajax-loader-small.gif (100%)
 rename {web => webv4}/root/images/cp437-ibm-vga8.png (100%)
 rename {web => webv4}/root/images/favicon.ico (100%)
 rename {web => webv4}/root/index.xjs (100%)
 rename {web => webv4}/root/js/avatars.js (100%)
 rename {web => webv4}/root/js/common.js (100%)
 rename {web => webv4}/root/js/forum.js (100%)
 rename {web => webv4}/root/js/graphics-converter.js (100%)
 rename {web => webv4}/root/js/jquery.min.js (100%)
 rename {web => webv4}/root/js/offcanvas.js (100%)
 rename {web => webv4}/root/js/validator.js (100%)
 rename {web => webv4}/sidebar/.examples/001-nodelist.xjs (100%)
 rename {web => webv4}/sidebar/.examples/002-recent-visitors.xjs (100%)
 rename {web => webv4}/sidebar/.examples/003-systemStats.xjs (100%)

diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 0667d37d9d..0000000000
--- a/.gitignore
+++ /dev/null
@@ -1,14 +0,0 @@
-# Ignore processed XJS files
-*.xjs.ssjs
-
-# Ignore non-example pages
-/web/pages/*
-!/web/pages/.examples
-
-# Ignore non-example sidebar modules
-/web/sidebar/*
-!/web/sidebar/.examples
-
-# Ignore non-example components
-/web/components/*
-!/web/components/.examples
diff --git a/README.md b/README.md
deleted file mode 100644
index 88a78b9282..0000000000
--- a/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# synchronet-web-v4
-A web interface for Synchronet BBS
-
-Documentation is available on [the wiki](https://github.com/echicken/synchronet-web-v4/wiki)
diff --git a/mods/js-date-format.js b/mods/js-date-format.js
deleted file mode 100644
index d21b35a596..0000000000
--- a/mods/js-date-format.js
+++ /dev/null
@@ -1,290 +0,0 @@
-/**
-* js-date-format (js-date-format.js)
-* v1.0
-* (c) Tony Brix (tonybrix.info) - 2014.
-* https://github.com/UziTech/js-date-format
-*
-* MIT License
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-* associated documentation files (the "Software"), to deal in the Software without restriction,
-* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
-* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
-* subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or substantial
-* portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
-* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-Date.prototype.setLocale = function (lang) {
-	if (lang && lang in Date.locales) {
-		this.locale = lang;
-	}
-};
-
-Date.prototype.getLocale = function () {
-	return this.locale || "en";
-};
-
-Date.prototype.getMonthName = function (lang) {
-	var locale = "en";
-	if (lang && lang in Date.locales) {
-		locale = lang;
-	} else if (this.locale && this.locale in Date.locales) {
-		locale = this.locale;
-	}
-	return Date.locales[locale].month_names[this.getMonth()];
-};
-
-Date.prototype.getMonthNameShort = function (lang) {
-	var locale = "en";
-	if (lang && lang in Date.locales) {
-		locale = lang;
-	} else if (this.locale && this.locale in Date.locales) {
-		locale = this.locale;
-	}
-	return Date.locales[locale].month_names_short[this.getMonth()];
-};
-
-Date.prototype.getDayName = function (lang) {
-	var locale = "en";
-	if (lang && lang in Date.locales) {
-		locale = lang;
-	} else if (this.locale && this.locale in Date.locales) {
-		locale = this.locale;
-	}
-	return Date.locales[locale].day_names[this.getDay()];
-};
-
-Date.prototype.getDayNameShort = function (lang) {
-	var locale = "en";
-	if (lang && lang in Date.locales) {
-		locale = lang;
-	} else if (this.locale && this.locale in Date.locales) {
-		locale = this.locale;
-	}
-	return Date.locales[locale].day_names_short[this.getDay()];
-};
-
-Date.prototype.getDateSuffix = function (lang) {
-	var locale = "en";
-	if (lang && lang in Date.locales) {
-		locale = lang;
-	} else if (this.locale && this.locale in Date.locales) {
-		locale = this.locale;
-	}
-	return Date.locales[locale].date_suffix(this.getDate());
-};
-
-Date.prototype.getMeridiem = function (isLower, lang) {
-	var locale = "en";
-	if (lang && lang in Date.locales) {
-		locale = lang;
-	} else if (this.locale && this.locale in Date.locales) {
-		locale = this.locale;
-	}
-	return Date.locales[locale].meridiem(this.getHours(), this.getMinutes(), isLower);
-};
-
-Date.prototype.getLastDate = function () {
-	return (new Date(this.getFullYear(), this.getMonth() + 1, 0)).getDate();
-};
-
-/* languages from http://momentjs.com */
-Date.locales = {
-	"en": {
-		month_names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
-		month_names_short: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
-		day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
-		day_names_short: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
-		date_suffix: function (date) {
-			var day10 = ~~ (date % 100 / 10);
-			var day1 = date % 10;
-			if (day10 === 1) {
-				return "th";
-			} else if (day1 === 1) {
-				return "st";
-			} else if (day1 === 2) {
-				return "nd";
-			} else if (day1 === 3) {
-				return "rd";
-			} else {
-				return "th";
-			}
-		},
-		meridiem : function (hour, minute, isLower) {
-			if (hour < 12) {
-				return isLower ? "am" : "AM";
-			} else {
-				return isLower ? "pm" : "PM";
-			}
-		}
-	}
-};
-
-Date.prototype.format = function (formatString) {
-
-	var addPadding = function (value, length) {
-		var negative = ((value < 0) ? "-" : "");
-		var zeros = "0";
-		for (var i = 2; i < length; i++) {
-			zeros += "0";
-		}
-		return negative + (zeros + Math.abs(value).toString()).slice(-length);
-	};
-
-	var replacements = {
-		date: this,
-		YYYY: function () {
-			return this.date.getFullYear();
-		},
-		YY: function () {
-			return this.date.getFullYear() % 100;
-		},
-		MMMM: function () {
-			return this.date.getMonthName();
-		},
-		MMM: function () {
-			return this.date.getMonthNameShort();
-		},
-		MM: function () {
-			return addPadding((this.date.getMonth() + 1), 2);
-		},
-		M: function () {
-			return this.date.getMonth() + 1;
-		},
-		DDDD: function () {
-			return this.date.getDayName();
-		},
-		DDD: function () {
-			return this.date.getDayNameShort();
-		},
-		DD: function () {
-			return addPadding(this.date.getDate(), 2);
-		},
-		D: function () {
-			return this.date.getDate();
-		},
-		S: function () {
-			return this.date.getDateSuffix();
-		},
-		HH: function () {
-			return addPadding(this.date.getHours(), 2);
-		},
-		H: function () {
-			return this.date.getHours();
-		},
-		hh: function () {
-			var hour = this.date.getHours();
-			if (hour > 12) {
-				hour -= 12;
-			} else if (hour < 1) {
-				hour = 12;
-			}
-			return addPadding(hour, 2);
-		},
-		h: function () {
-			var hour = this.date.getHours();
-			if (hour > 12) {
-				hour -= 12;
-			} else if (hour < 1) {
-				hour = 12;
-			}
-			return hour;
-		},
-		mm: function () {
-			return addPadding(this.date.getMinutes(), 2);
-		},
-		m: function () {
-			return this.date.getMinutes();
-		},
-		ss: function () {
-			return addPadding(this.date.getSeconds(), 2);
-		},
-		s: function () {
-			return this.date.getSeconds();
-		},
-		fff: function () {
-			return addPadding(this.date.getMilliseconds(), 3);
-		},
-		ff: function () {
-			return addPadding(Math.floor(this.date.getMilliseconds() / 10), 2);
-		},
-		f: function () {
-			return Math.floor(this.date.getMilliseconds() / 100);
-		},
-		zzzz: function () {
-			return addPadding(Math.floor(-this.date.getTimezoneOffset() / 60), 2) + ":" + addPadding(-this.date.getTimezoneOffset() % 60, 2);
-		},
-		zzz: function () {
-			return Math.floor(-this.date.getTimezoneOffset() / 60) + ":" + addPadding(-this.date.getTimezoneOffset() % 60, 2);
-		},
-		zz: function () {
-			return addPadding(Math.floor(-this.date.getTimezoneOffset() / 60), 2);
-		},
-		z: function () {
-			return Math.floor(-this.date.getTimezoneOffset() / 60);
-		},
-		tt: function () {
-			return this.date.getMeridiem(true);
-		},
-		TT: function () {
-			return this.date.getMeridiem(false);
-		}
-	};
-
-
-	var formats = new Array();
-	while (formatString.length > 0) {
-		if (formatString[0] === "\"") {
-			var temp = /"[^"]*"/m.exec(formatString);
-			if (temp === null) {
-				formats.push(formatString.substring(1));
-				formatString = "";
-			} else {
-				temp = temp[0].substring(1, temp[0].length - 1);
-				formats.push(temp);
-				formatString = formatString.substring(temp.length + 2);
-			}
-		} else if (formatString[0] === "'") {
-			var temp = /'[^']*'/m.exec(formatString);
-			if (temp === null) {
-				formats.push(formatString.substring(1));
-				formatString = "";
-			} else {
-				temp = temp[0].substring(1, temp[0].length - 1);
-				formats.push(temp);
-				formatString = formatString.substring(temp.length + 2);
-			}
-		} else if (formatString[0] === "\\") {
-			if (formatString.length > 1) {
-				formats.push(formatString.substring(1, 2));
-				formatString = formatString.substring(2);
-			} else {
-				formats.push("\\");
-				formatString = "";
-			}
-		} else {
-			var foundMatch = false;
-			for (var i = formatString.length; i > 0; i--) {
-				if (formatString.substring(0, i) in replacements) {
-					formats.push(replacements[formatString.substring(0, i)]());
-					formatString = formatString.substring(i);
-					foundMatch = true;
-					break;
-				}
-			}
-			if (!foundMatch) {
-				formats.push(formatString[0]);
-				formatString = formatString.substring(1);
-			}
-		}
-	}
-
-	return formats.join("");
-};
\ No newline at end of file
diff --git a/mods/webv4-installer.js b/mods/webv4-installer.js
deleted file mode 100644
index 3196ccdb88..0000000000
--- a/mods/webv4-installer.js
+++ /dev/null
@@ -1,276 +0,0 @@
-const named_parameters = {};
-if (argv.length > 0) {
-  argv.forEach(function (e) {
-    const m = /-?((\w+)(?:=("[^"]+"|[^\s"]+))?)(?:\s+|$)/.exec(e);
-    named_parameters[m[2]] = (m[3] ? m[3] : null);
-  });
-}
-
-load('http.js');
-
-function download(url, target) {
-	const http = new HTTPRequest();
-    try {
-	    const zip = http.Get(url);
-    } catch (err) {
-        log(err);
-        return false;
-    }
-	const f = new File(target);
-	if (!f.open('wb')) return false;
-	if (!f.write(zip)) return false;
-	f.close();
-    return true;
-}
-
-function extract(file, target) {
-	if (!file_isdir(install_dir)) {
-    if (!mkdir(install_dir)) return false;
-  }
-  const zp = system.platform.search(/^win/i) > -1 ? system.exec_dir : '';
-	return system.exec(zp + 'unzip -uqo ' + file + ' -d ' + target) == 0;
-}
-
-function update_sbbs_ini(root_directory, error_directory) {
-	if (!file_backup(system.ctrl_dir + 'sbbs.ini')) return false;
-	const f = new File(system.ctrl_dir + 'sbbs.ini');
-	if (!f.open('r+')) return false;
-	if (!f.iniSetValue('Web', 'RootDirectory', root_directory)) return false;
-	if (!f.iniSetValue('Web', 'ErrorDirectory', error_directory)) return false;
-	f.close();
-    return true;
-}
-
-function update_modopts_ini(modopts) {
-	if (!file_backup(system.ctrl_dir + 'modopts.ini')) return false;
-	const f = new File(system.ctrl_dir + 'modopts.ini');
-	if (!f.open('r+')) return false;
-	if (!f.iniSetObject('web', modopts)) return false;
-	f.close();
-    return true;
-}
-
-function get_modopts_ini() {
-    const f = new File(system.ctrl_dir + 'modopts.ini');
-    if (!f.open('r')) return false;
-    const ini = f.iniGetObject('web');
-    f.close();
-    return ini;
-}
-
-function get_service_ini(section) {
-    const f = new File(system.ctrl_dir + 'services.ini');
-    if (!f.open('r')) return false;
-    const ini = f.iniGetObject(section);
-    f.close();
-    return ini;
-}
-
-function set_service_ini(section, obj) {
-    const f = new File(system.ctrl_dir + 'services.ini');
-    if (!f.open('r+')) return false;
-    if (!f.iniSetObject(section, obj)) return false;
-    f.close();
-    return true;
-}
-
-function get_setting(text, value) {
-  if (typeof named_parameters.defaults != 'undefined') return value;
-  const i = prompt(text + ' [' + value + ']');
-  return (i == '' ? value : i);
-}
-
-function confirm_setting(text, value) {
-  if (typeof named_parameters.defaults != 'undefined') {
-    return value;
-  } else if (!value) {
-    return !deny(text + ' [' + value + ']');
-  } else {
-    return confirm(text + ' [' + value + ']');
-  }
-}
-
-function copy_dir_contents(src, dest, overwrite) {
-    src = backslash(fullpath(src));
-    dest = backslash(fullpath(dest));
-    const delim = src.substr(-1);
-    if (!file_isdir(dest)) mkdir(dest);
-    directory(src + '*').forEach(
-    	function (e) {
-    		e = fullpath(e);
-    		if (file_isdir(e)) {
-    			const path = e.split(delim);
-    			var df = dest + path[path.length - 2];
-    			if (!file_isdir(df)) mkdir(df);
-    			copy_dir_contents(e, df, overwrite);
-    		} else {
-    			var df = dest + file_getname(e);
-    			if (!file_exists(df) || overwrite) {
-					file_copy(e, dest + file_getname(e));
-    			}
-    		}
-    	}
-    );
-}
-
-// yikes
-function remove_dir(dir) {
-    dir = backslash(fullpath(dir));
-    directory(dir + '*').forEach(
-    	function (e) {
-    		if (file_isdir(e)) {
-    			remove_dir(e);
-    		} else {
-	    		file_remove(e);
-	    	}
-    	}
-    );
-    rmdir(dir);
-}
-
-const url_suffix = named_parameters.release ? named_parameters.release : 'master';
-const zip_url = 'https://codeload.github.com/echicken/synchronet-web-v4/zip/' + url_suffix;
-const download_target = system.temp_dir + 'webv4.zip';
-const extract_dir = fullpath(system.temp_dir);
-const temp_dir = fullpath(extract_dir + '/synchronet-web-v4-master');
-const install_dir = fullpath(system.exec_dir + '../webv4');
-const root_directory = fullpath(install_dir + '/root');
-const error_directory = fullpath(root_directory + '/error');
-
-var modopts_web = get_modopts_ini();
-if (!modopts_web) {
-    modopts_web = {
-    	guest : 'Guest',
-    	timeout : 43200,
-    	inactivity : 900,
-    	user_registration : false,
-    	minimum_password_length : 6,
-    	maximum_telegram_length : 800,
-    	web_directory : install_dir,
-        ftelnet : true,
-    	ftelnet_splash : '../text/synch.ans',
-    	keyboard_navigation : false,
-    	vote_functions : true,
-    	refresh_interval : 60000,
-    	xtrn_blacklist : 'scfg,oneliner',
-    	layout_sidebar_off : false,
-    	layout_sidebar_left : false,
-    	layout_full_width : false,
-    	forum_extended_ascii : false,
-    	max_messages : 0
-    };
-}
-
-var wss = get_service_ini('WS');
-if (!wss) {
-    var wss = {
-        Port : 1123,
-        Options : 'NO_HOST_LOOKUP',
-        Command : 'websocketservice.js'
-    };
-}
-
-var wsss = get_service_ini('WSS');
-if (!wsss) {
-    var wsss = {
-        Port : 11235,
-        Options : 'NO_HOST_LOOKUP|TLS',
-        Command : 'websocketservice.js'
-    };
-}
-
-write('\r\n---\r\n\r\n');
-writeln('ecwebv4 installer/updater');
-writeln('https://github.com/echicken/synchronet-web-v4');
-
-if (system.version_num < 31700) {
-    writeln('Synchronet versions earlier than 3.17a are not supported. Exiting.');
-    exit();
-}
-
-write('\r\nIt is strongly recommended that you back up your BBS before proceeding.\r\n');
-write('\r\nIf this is a new intallation, you must also shut down your BBS now.\r\n\r\n');
-
-if (typeof named_parameters.defaults == 'undefined' && deny('Proceed with installation/update')) {
-    writeln('Install/update aborted.  Exiting.');
-    exit();
-}
-write('\r\n\r\n---\r\n\r\n');
-
-if (!file_exists(download_target) || confirm(download_target + ' present.  Overwrite')) {
-    writeln('Downloading ' + zip_url);
-    if (!download(zip_url, download_target)) {
-        writeln('Download of ' + zip_url + ' failed. Exiting.');
-        exit();
-    }
-}
-
-writeln('Extracting ' + download_target);
-if (!extract(download_target, extract_dir)) {
-    writeln('Extraction of ' + download_target + ' failed. Exiting.');
-    exit();
-}
-
-writeln('Copying files ...');
-copy_dir_contents(temp_dir + '/mods', system.mods_dir, true);
-copy_dir_contents(temp_dir + '/text', system.text_dir, true);
-copy_dir_contents(temp_dir + '/web', install_dir, true);
-copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages', false);
-copy_dir_contents(temp_dir + '/web/pages/.examples', install_dir + '/pages/.examples', true); // These ... probably aren't necessary
-copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar', false);
-copy_dir_contents(temp_dir + '/web/sidebar/.examples', install_dir + '/sidebar/.examples', true); // These ... probably aren't necessary
-copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components', false);
-copy_dir_contents(temp_dir + '/web/components/.examples', install_dir + '/components/.examples', true); // These ... probably aren't necessary
-
-writeln('Cleaning up ...');
-remove_dir(temp_dir + '/web/pages/.examples');
-remove_dir(temp_dir + '/web/sidebar/.examples');
-remove_dir(temp_dir);
-file_remove(download_target);
-
-write('\r\n---\r\n\r\n');
-write('Configuration - press enter to accept default/current value.\r\n\r\n');
-modopts_web.guest = get_setting('Guest user alias', modopts_web.guest);
-if (!system.matchuser(modopts_web.guest)) {
-    writeln('Guest user does not exist. Exiting.');
-    exit();
-}
-modopts_web.user_registration = confirm_setting('Allow new user registration via the web', modopts_web.user_registration);
-modopts_web.ftelnet = confirm_setting('Enable fTelnet', modopts_web.ftelnet);
-if (modopts_web.ftelnet) {
-    modopts_web.ftelnet_splash = get_setting('Path to ftelnet background .ans', modopts_web.ftelnet_splash);
-    write('\r\nUse of fTelnet requires a WebSocket proxy service.\r\n');
-    writeln('A websocket proxy server routes traffic between a browser-based');
-    writeln('application and some other arbitrary server.  Here you will configure');
-    writeln('the ports that your WebSocket and WebSocket Secure proxy services will');
-    writeln('listen on. Be sure to open these ports in your firewall.');
-    write('\r\n');
-    wss.Port = get_setting('WebSocket service port for HTTP clients', wss.Port);
-    wsss.Port = get_setting('WebSocket secure service port for HTTPS clients', wsss.Port);
-    writeln('Updating services.ini ...');
-    if (!file_backup(system.ctrl_dir + 'services.ini')) {
-        writeln('Failed to back up services.ini.  Exiting.');
-        exit();
-    }
-    if(!set_service_ini('WS', wss)) writeln('Failed to configure WS service.');
-    if (!set_service_ini('WSS', wsss)) writeln('Failed to configure WSS service.');
-}
-
-writeln('Updating modopts.ini ...');
-if (!update_modopts_ini(modopts_web)) {
-    writeln('Failed to update modopts.ini. Exiting.');
-    exit();
-}
-
-writeln('Updating sbbs.ini ...');
-if (!update_sbbs_ini(root_directory, error_directory)) {
-    writeln('Failed to update sbbs.ini. Exiting.');
-    exit();
-} else {
-    write('\r\n---\r\n\r\n');
-    writeln('Install/update complete.');
-    writeln('If you shut down your BBS, you can restart it now.');
-    writeln('For additional configuration and customization steps,');
-    writeln('visit https://github.com/echicken/synchronet-web-v4');
-    write('\r\n\r\n---\r\n\r\n');
-}
diff --git a/text/synch.ans b/text/synch.ans
deleted file mode 100644
index fada940771..0000000000
--- a/text/synch.ans
+++ /dev/null
@@ -1,21 +0,0 @@
-
-   ��   �
-  �  �ܰ�  ��gj/��
-synchronet   �� �   �  ߲���   ��   ��
-� �    ������    ������  �������� �������� ���������   �  ����
-    ��   �������۱�  ������� ��� ���� ��� ��������   ������  ������ ��
-   ��   ��������۲� ����� ����� �������۲� �� �����������۲� ��۰ ������
-    �  ������� ����� �� � ������� ��������  ���������� ����� ��۲��������� �
-   ���� ��������  ������� �������  � ��۲� ����������� ���۱������������ � ��
-  ��� ����� ������ ����� �����������  ��� ������ ���� � ��������۲� ��������
- ������� ��� � ������������۲����������������۱  �����  �����  ����� ����
-  ��߲��������� �� ��������۱ ���� ���۲�� ������ �� ܱ� ����������� ���
-  �����۲��� ����� �����������  ��۱�� � �� ����۲����������������
-    ��  ���������� ���� ���� ��������� ����� � ��������  ��� � ����� �
-�  �  ������������ ������  � � ���� ����� � ���   ��� � �����
-   � ��  ���۲�����   �� �߰����    � �
-���� �ݰ�   bbs software
-����  �
-����
-��
-�
diff --git a/web/components/.examples/footer.xjs b/webv4/components/.examples/footer.xjs
similarity index 100%
rename from web/components/.examples/footer.xjs
rename to webv4/components/.examples/footer.xjs
diff --git a/web/components/.examples/header.xjs b/webv4/components/.examples/header.xjs
similarity index 100%
rename from web/components/.examples/header.xjs
rename to webv4/components/.examples/header.xjs
diff --git a/web/components/.examples/modal.xjs b/webv4/components/.examples/modal.xjs
similarity index 100%
rename from web/components/.examples/modal.xjs
rename to webv4/components/.examples/modal.xjs
diff --git a/web/components/.examples/navbar.xjs b/webv4/components/.examples/navbar.xjs
similarity index 100%
rename from web/components/.examples/navbar.xjs
rename to webv4/components/.examples/navbar.xjs
diff --git a/web/lib/auth.js b/webv4/lib/auth.js
similarity index 100%
rename from web/lib/auth.js
rename to webv4/lib/auth.js
diff --git a/web/lib/avatars.js b/webv4/lib/avatars.js
similarity index 100%
rename from web/lib/avatars.js
rename to webv4/lib/avatars.js
diff --git a/web/lib/events/forum.js b/webv4/lib/events/forum.js
similarity index 100%
rename from web/lib/events/forum.js
rename to webv4/lib/events/forum.js
diff --git a/web/lib/events/mail.js b/webv4/lib/events/mail.js
similarity index 100%
rename from web/lib/events/mail.js
rename to webv4/lib/events/mail.js
diff --git a/web/lib/events/nodelist.js b/webv4/lib/events/nodelist.js
similarity index 100%
rename from web/lib/events/nodelist.js
rename to webv4/lib/events/nodelist.js
diff --git a/web/lib/events/sbbsimsg.js b/webv4/lib/events/sbbsimsg.js
similarity index 100%
rename from web/lib/events/sbbsimsg.js
rename to webv4/lib/events/sbbsimsg.js
diff --git a/web/lib/events/telegram.js b/webv4/lib/events/telegram.js
similarity index 100%
rename from web/lib/events/telegram.js
rename to webv4/lib/events/telegram.js
diff --git a/web/lib/files.js b/webv4/lib/files.js
similarity index 100%
rename from web/lib/files.js
rename to webv4/lib/files.js
diff --git a/web/lib/forum.js b/webv4/lib/forum.js
similarity index 100%
rename from web/lib/forum.js
rename to webv4/lib/forum.js
diff --git a/web/lib/ftelnet.js b/webv4/lib/ftelnet.js
similarity index 100%
rename from web/lib/ftelnet.js
rename to webv4/lib/ftelnet.js
diff --git a/web/lib/init.js b/webv4/lib/init.js
similarity index 100%
rename from web/lib/init.js
rename to webv4/lib/init.js
diff --git a/web/lib/locale.js b/webv4/lib/locale.js
similarity index 100%
rename from web/lib/locale.js
rename to webv4/lib/locale.js
diff --git a/web/lib/locale/en_ca.ini b/webv4/lib/locale/en_ca.ini
similarity index 100%
rename from web/lib/locale/en_ca.ini
rename to webv4/lib/locale/en_ca.ini
diff --git a/web/lib/locale/en_ca.js b/webv4/lib/locale/en_ca.js
similarity index 100%
rename from web/lib/locale/en_ca.js
rename to webv4/lib/locale/en_ca.js
diff --git a/web/lib/locale/en_us.ini b/webv4/lib/locale/en_us.ini
similarity index 100%
rename from web/lib/locale/en_us.ini
rename to webv4/lib/locale/en_us.ini
diff --git a/web/lib/locale/en_us.js b/webv4/lib/locale/en_us.js
similarity index 100%
rename from web/lib/locale/en_us.js
rename to webv4/lib/locale/en_us.js
diff --git a/web/lib/locale/es_ar.ini b/webv4/lib/locale/es_ar.ini
similarity index 100%
rename from web/lib/locale/es_ar.ini
rename to webv4/lib/locale/es_ar.ini
diff --git a/web/lib/mime-decode.js b/webv4/lib/mime-decode.js
similarity index 100%
rename from web/lib/mime-decode.js
rename to webv4/lib/mime-decode.js
diff --git a/web/lib/pages.js b/webv4/lib/pages.js
similarity index 100%
rename from web/lib/pages.js
rename to webv4/lib/pages.js
diff --git a/web/lib/request.js b/webv4/lib/request.js
similarity index 100%
rename from web/lib/request.js
rename to webv4/lib/request.js
diff --git a/web/lib/sidebar.js b/webv4/lib/sidebar.js
similarity index 100%
rename from web/lib/sidebar.js
rename to webv4/lib/sidebar.js
diff --git a/web/pages/.examples/000-home.xjs b/webv4/pages/.examples/000-home.xjs
similarity index 100%
rename from web/pages/.examples/000-home.xjs
rename to webv4/pages/.examples/000-home.xjs
diff --git a/web/pages/.examples/000-mail.xjs b/webv4/pages/.examples/000-mail.xjs
similarity index 100%
rename from web/pages/.examples/000-mail.xjs
rename to webv4/pages/.examples/000-mail.xjs
diff --git a/web/pages/.examples/000-register.xjs b/webv4/pages/.examples/000-register.xjs
similarity index 100%
rename from web/pages/.examples/000-register.xjs
rename to webv4/pages/.examples/000-register.xjs
diff --git a/web/pages/.examples/001-forum.ssjs b/webv4/pages/.examples/001-forum.ssjs
similarity index 100%
rename from web/pages/.examples/001-forum.ssjs
rename to webv4/pages/.examples/001-forum.ssjs
diff --git a/web/pages/.examples/001-forum.xjs b/webv4/pages/.examples/001-forum.xjs
similarity index 100%
rename from web/pages/.examples/001-forum.xjs
rename to webv4/pages/.examples/001-forum.xjs
diff --git a/web/pages/.examples/002-files.xjs b/webv4/pages/.examples/002-files.xjs
similarity index 100%
rename from web/pages/.examples/002-files.xjs
rename to webv4/pages/.examples/002-files.xjs
diff --git a/web/pages/.examples/003-games.xjs b/webv4/pages/.examples/003-games.xjs
similarity index 100%
rename from web/pages/.examples/003-games.xjs
rename to webv4/pages/.examples/003-games.xjs
diff --git a/web/pages/.examples/More/001-userlist.xjs b/webv4/pages/.examples/More/001-userlist.xjs
similarity index 100%
rename from web/pages/.examples/More/001-userlist.xjs
rename to webv4/pages/.examples/More/001-userlist.xjs
diff --git a/web/pages/.examples/More/999-sbbslist.xjs b/webv4/pages/.examples/More/999-sbbslist.xjs
similarity index 100%
rename from web/pages/.examples/More/999-sbbslist.xjs
rename to webv4/pages/.examples/More/999-sbbslist.xjs
diff --git a/web/pages/.examples/More/webctrl.ini b/webv4/pages/.examples/More/webctrl.ini
similarity index 100%
rename from web/pages/.examples/More/webctrl.ini
rename to webv4/pages/.examples/More/webctrl.ini
diff --git a/web/pages/.examples/webctrl.ini b/webv4/pages/.examples/webctrl.ini
similarity index 100%
rename from web/pages/.examples/webctrl.ini
rename to webv4/pages/.examples/webctrl.ini
diff --git a/web/root/api/attachments.ssjs b/webv4/root/api/attachments.ssjs
similarity index 100%
rename from web/root/api/attachments.ssjs
rename to webv4/root/api/attachments.ssjs
diff --git a/web/root/api/auth.ssjs b/webv4/root/api/auth.ssjs
similarity index 100%
rename from web/root/api/auth.ssjs
rename to webv4/root/api/auth.ssjs
diff --git a/web/root/api/events.ssjs b/webv4/root/api/events.ssjs
similarity index 100%
rename from web/root/api/events.ssjs
rename to webv4/root/api/events.ssjs
diff --git a/web/root/api/files.ssjs b/webv4/root/api/files.ssjs
similarity index 100%
rename from web/root/api/files.ssjs
rename to webv4/root/api/files.ssjs
diff --git a/web/root/api/forum.ssjs b/webv4/root/api/forum.ssjs
similarity index 100%
rename from web/root/api/forum.ssjs
rename to webv4/root/api/forum.ssjs
diff --git a/web/root/api/github.ssjs b/webv4/root/api/github.ssjs
similarity index 100%
rename from web/root/api/github.ssjs
rename to webv4/root/api/github.ssjs
diff --git a/web/root/api/register.ssjs b/webv4/root/api/register.ssjs
similarity index 100%
rename from web/root/api/register.ssjs
rename to webv4/root/api/register.ssjs
diff --git a/web/root/api/sbbsimsg.ssjs b/webv4/root/api/sbbsimsg.ssjs
similarity index 100%
rename from web/root/api/sbbsimsg.ssjs
rename to webv4/root/api/sbbsimsg.ssjs
diff --git a/web/root/api/system.ssjs b/webv4/root/api/system.ssjs
similarity index 100%
rename from web/root/api/system.ssjs
rename to webv4/root/api/system.ssjs
diff --git a/web/root/bootstrap/css/bootstrap-theme.css b/webv4/root/bootstrap/css/bootstrap-theme.css
similarity index 100%
rename from web/root/bootstrap/css/bootstrap-theme.css
rename to webv4/root/bootstrap/css/bootstrap-theme.css
diff --git a/web/root/bootstrap/css/bootstrap-theme.css.map b/webv4/root/bootstrap/css/bootstrap-theme.css.map
similarity index 100%
rename from web/root/bootstrap/css/bootstrap-theme.css.map
rename to webv4/root/bootstrap/css/bootstrap-theme.css.map
diff --git a/web/root/bootstrap/css/bootstrap-theme.min.css b/webv4/root/bootstrap/css/bootstrap-theme.min.css
similarity index 100%
rename from web/root/bootstrap/css/bootstrap-theme.min.css
rename to webv4/root/bootstrap/css/bootstrap-theme.min.css
diff --git a/web/root/bootstrap/css/bootstrap.css b/webv4/root/bootstrap/css/bootstrap.css
similarity index 100%
rename from web/root/bootstrap/css/bootstrap.css
rename to webv4/root/bootstrap/css/bootstrap.css
diff --git a/web/root/bootstrap/css/bootstrap.css.map b/webv4/root/bootstrap/css/bootstrap.css.map
similarity index 100%
rename from web/root/bootstrap/css/bootstrap.css.map
rename to webv4/root/bootstrap/css/bootstrap.css.map
diff --git a/web/root/bootstrap/css/bootstrap.min.css b/webv4/root/bootstrap/css/bootstrap.min.css
similarity index 100%
rename from web/root/bootstrap/css/bootstrap.min.css
rename to webv4/root/bootstrap/css/bootstrap.min.css
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.eot b/webv4/root/bootstrap/fonts/glyphicons-halflings-regular.eot
similarity index 100%
rename from web/root/bootstrap/fonts/glyphicons-halflings-regular.eot
rename to webv4/root/bootstrap/fonts/glyphicons-halflings-regular.eot
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.svg b/webv4/root/bootstrap/fonts/glyphicons-halflings-regular.svg
similarity index 100%
rename from web/root/bootstrap/fonts/glyphicons-halflings-regular.svg
rename to webv4/root/bootstrap/fonts/glyphicons-halflings-regular.svg
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.ttf b/webv4/root/bootstrap/fonts/glyphicons-halflings-regular.ttf
similarity index 100%
rename from web/root/bootstrap/fonts/glyphicons-halflings-regular.ttf
rename to webv4/root/bootstrap/fonts/glyphicons-halflings-regular.ttf
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.woff b/webv4/root/bootstrap/fonts/glyphicons-halflings-regular.woff
similarity index 100%
rename from web/root/bootstrap/fonts/glyphicons-halflings-regular.woff
rename to webv4/root/bootstrap/fonts/glyphicons-halflings-regular.woff
diff --git a/web/root/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/webv4/root/bootstrap/fonts/glyphicons-halflings-regular.woff2
similarity index 100%
rename from web/root/bootstrap/fonts/glyphicons-halflings-regular.woff2
rename to webv4/root/bootstrap/fonts/glyphicons-halflings-regular.woff2
diff --git a/web/root/bootstrap/js/bootstrap.js b/webv4/root/bootstrap/js/bootstrap.js
similarity index 100%
rename from web/root/bootstrap/js/bootstrap.js
rename to webv4/root/bootstrap/js/bootstrap.js
diff --git a/web/root/bootstrap/js/bootstrap.min.js b/webv4/root/bootstrap/js/bootstrap.min.js
similarity index 100%
rename from web/root/bootstrap/js/bootstrap.min.js
rename to webv4/root/bootstrap/js/bootstrap.min.js
diff --git a/web/root/bootstrap/js/npm.js b/webv4/root/bootstrap/js/npm.js
similarity index 100%
rename from web/root/bootstrap/js/npm.js
rename to webv4/root/bootstrap/js/npm.js
diff --git a/web/root/css/navbar-fixed-top.css b/webv4/root/css/navbar-fixed-top.css
similarity index 100%
rename from web/root/css/navbar-fixed-top.css
rename to webv4/root/css/navbar-fixed-top.css
diff --git a/web/root/css/offcanvas.css b/webv4/root/css/offcanvas.css
similarity index 100%
rename from web/root/css/offcanvas.css
rename to webv4/root/css/offcanvas.css
diff --git a/web/root/css/style.css b/webv4/root/css/style.css
similarity index 100%
rename from web/root/css/style.css
rename to webv4/root/css/style.css
diff --git a/web/root/error/400.html b/webv4/root/error/400.html
similarity index 100%
rename from web/root/error/400.html
rename to webv4/root/error/400.html
diff --git a/web/root/error/401.html b/webv4/root/error/401.html
similarity index 100%
rename from web/root/error/401.html
rename to webv4/root/error/401.html
diff --git a/web/root/error/403.html b/webv4/root/error/403.html
similarity index 100%
rename from web/root/error/403.html
rename to webv4/root/error/403.html
diff --git a/web/root/error/404.html b/webv4/root/error/404.html
similarity index 100%
rename from web/root/error/404.html
rename to webv4/root/error/404.html
diff --git a/web/root/error/414.html b/webv4/root/error/414.html
similarity index 100%
rename from web/root/error/414.html
rename to webv4/root/error/414.html
diff --git a/web/root/error/416.html b/webv4/root/error/416.html
similarity index 100%
rename from web/root/error/416.html
rename to webv4/root/error/416.html
diff --git a/web/root/error/500.html b/webv4/root/error/500.html
similarity index 100%
rename from web/root/error/500.html
rename to webv4/root/error/500.html
diff --git a/web/root/error/501.html b/webv4/root/error/501.html
similarity index 100%
rename from web/root/error/501.html
rename to webv4/root/error/501.html
diff --git a/web/root/images/ajax-loader-small.gif b/webv4/root/images/ajax-loader-small.gif
similarity index 100%
rename from web/root/images/ajax-loader-small.gif
rename to webv4/root/images/ajax-loader-small.gif
diff --git a/web/root/images/cp437-ibm-vga8.png b/webv4/root/images/cp437-ibm-vga8.png
similarity index 100%
rename from web/root/images/cp437-ibm-vga8.png
rename to webv4/root/images/cp437-ibm-vga8.png
diff --git a/web/root/images/favicon.ico b/webv4/root/images/favicon.ico
similarity index 100%
rename from web/root/images/favicon.ico
rename to webv4/root/images/favicon.ico
diff --git a/web/root/index.xjs b/webv4/root/index.xjs
similarity index 100%
rename from web/root/index.xjs
rename to webv4/root/index.xjs
diff --git a/web/root/js/avatars.js b/webv4/root/js/avatars.js
similarity index 100%
rename from web/root/js/avatars.js
rename to webv4/root/js/avatars.js
diff --git a/web/root/js/common.js b/webv4/root/js/common.js
similarity index 100%
rename from web/root/js/common.js
rename to webv4/root/js/common.js
diff --git a/web/root/js/forum.js b/webv4/root/js/forum.js
similarity index 100%
rename from web/root/js/forum.js
rename to webv4/root/js/forum.js
diff --git a/web/root/js/graphics-converter.js b/webv4/root/js/graphics-converter.js
similarity index 100%
rename from web/root/js/graphics-converter.js
rename to webv4/root/js/graphics-converter.js
diff --git a/web/root/js/jquery.min.js b/webv4/root/js/jquery.min.js
similarity index 100%
rename from web/root/js/jquery.min.js
rename to webv4/root/js/jquery.min.js
diff --git a/web/root/js/offcanvas.js b/webv4/root/js/offcanvas.js
similarity index 100%
rename from web/root/js/offcanvas.js
rename to webv4/root/js/offcanvas.js
diff --git a/web/root/js/validator.js b/webv4/root/js/validator.js
similarity index 100%
rename from web/root/js/validator.js
rename to webv4/root/js/validator.js
diff --git a/web/sidebar/.examples/001-nodelist.xjs b/webv4/sidebar/.examples/001-nodelist.xjs
similarity index 100%
rename from web/sidebar/.examples/001-nodelist.xjs
rename to webv4/sidebar/.examples/001-nodelist.xjs
diff --git a/web/sidebar/.examples/002-recent-visitors.xjs b/webv4/sidebar/.examples/002-recent-visitors.xjs
similarity index 100%
rename from web/sidebar/.examples/002-recent-visitors.xjs
rename to webv4/sidebar/.examples/002-recent-visitors.xjs
diff --git a/web/sidebar/.examples/003-systemStats.xjs b/webv4/sidebar/.examples/003-systemStats.xjs
similarity index 100%
rename from web/sidebar/.examples/003-systemStats.xjs
rename to webv4/sidebar/.examples/003-systemStats.xjs
-- 
GitLab


From 77ee80ba1b9aa6f75eb103841370c74a923ffed9 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 00:38:05 -0700
Subject: [PATCH 603/752] ecWebv4 ("webv4") is now merged with the sbbs repo.

---
 install/install.iss | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/install/install.iss b/install/install.iss
index 624504dab4..303f67b654 100644
--- a/install/install.iss
+++ b/install/install.iss
@@ -87,10 +87,10 @@ Source: "text\*";   DestDir: "{app}\text";  Flags: ignoreversion recursesubdirs
 Source: "xtrn\*";   DestDir: "{app}\xtrn";  Flags: ignoreversion recursesubdirs createallsubdirs
 Source: "docs\*";   DestDir: "{app}\docs";  Flags: ignoreversion recursesubdirs createallsubdirs
 Source: "web\*";    DestDir: "{app}\web";   Flags: ignoreversion recursesubdirs createallsubdirs
-Source: "e:\webv4\web\*";                                     DestDir: "{app}\webv4";            Flags: ignoreversion recursesubdirs createallsubdirs
-Source: "e:\webv4\web\pages\.examples\*";                     DestDir: "{app}\webv4\pages";      Flags: ignoreversion recursesubdirs
-Source: "e:\webv4\web\components\.examples\*";                DestDir: "{app}\webv4\components"; Flags: ignoreversion recursesubdirs
-Source: "e:\webv4\web\sidebar\.examples\*";                   DestDir: "{app}\webv4\sidebar";    Flags: ignoreversion recursesubdirs
+Source: "webv4\*";                                            DestDir: "{app}\webv4";            Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "webv4\pages\.examples\*";                            DestDir: "{app}\webv4\pages";      Flags: ignoreversion recursesubdirs
+Source: "webv4\components\.examples\*";                       DestDir: "{app}\webv4\components"; Flags: ignoreversion recursesubdirs
+Source: "webv4\sidebar\.examples\*";                          DestDir: "{app}\webv4\sidebar";    Flags: ignoreversion recursesubdirs
 Source: "c:\bin\zip.exe";                                     DestDir: "{app}\exec";  Flags: ignoreversion
 Source: "c:\bin\unzip.exe";                                   DestDir: "{app}\exec";  Flags: ignoreversion
 ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
-- 
GitLab


From 222250e7c27c9f6362bc08c110d5ec99b2ea92d9 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 00:54:07 -0700
Subject: [PATCH 604/752] echicken's webv4 is now merged into the sbbs repo.

---
 README.md | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index ad8b192f31..15b861f2f9 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,17 @@
 ## BBS-Related Software Source Repository
 Directories within:
 
- - `3rdp` - Third-party libraries
- - `ctrl` - Synchronet BBS configuration and run-time data files
- - `docs` - Synchronet BBS documentation (mostly legacy HTML)
- - `exec` - Synchronet BBS executable files (mostly JavaScript)
+ - `3rdp   ` - Third-party libraries
+ - `ctrl   ` - Synchronet BBS configuration and run-time data files
+ - `docs   ` - Synchronet BBS documentation (mostly legacy HTML)
+ - `exec   ` - Synchronet BBS executable files (mostly JavaScript)
  - `install` - Synchronet BBS  installation files
- - `node1` - Synchronet BBS Terminal Server "node" configuration files
- - `src` - Source code (mostly C/C++)
- - `text` - Synchronet BBS text and menu files
- - `web` - Synchronet Legacy/Runemaster web UI
- - `xtrn` - Synchronet BBS doors (mostly JavaScript)
+ - `node1  ` - Synchronet BBS Terminal Server "node" configuration files
+ - `src    ` - Source code (mostly C/C++)
+ - `text   ` - Synchronet BBS text and menu files
+ - `web    ` - Synchronet Legacy/Runemaster web UI
+ - `webv4  ` - echicken's web interface (v4) for Synchronet
+ - `xtrn   ` - Synchronet BBS doors (mostly JavaScript)
 
 Related web-sites:  
 [Synchronet BBS Software](http://www.synchro.net)  
-- 
GitLab


From 6e61b3c7e9f563bf9bd0964cee607fce1f7b9080 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 12:55:46 -0700
Subject: [PATCH 605/752] Remove 16-bit DOS program.

---
 xtrn/lord/menus/nuktown/readme.exe | Bin 82881 -> 0 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 xtrn/lord/menus/nuktown/readme.exe

diff --git a/xtrn/lord/menus/nuktown/readme.exe b/xtrn/lord/menus/nuktown/readme.exe
deleted file mode 100644
index 53503b5d70386fb795278d5abcacaef765d7cf74..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 82881
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

-- 
GitLab


From 7af7bb67848a5326599a502ae73eafd1cf5df99d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 13:09:10 -0700
Subject: [PATCH 606/752] Install instructions (for install-xtrn.js)

---
 xtrn/bullshit/install-xtrn.ini | 12 ++++++++++++
 xtrn/slyvote/install-xtrn.ini  | 10 ++++++++++
 xtrn/war/install-xtrn.ini      | 30 ++++++++++++++++++++++++++++++
 xtrn/wordem/install-xtrn.ini   | 17 +++++++++++++++++
 4 files changed, 69 insertions(+)
 create mode 100644 xtrn/bullshit/install-xtrn.ini
 create mode 100644 xtrn/slyvote/install-xtrn.ini
 create mode 100644 xtrn/war/install-xtrn.ini
 create mode 100644 xtrn/wordem/install-xtrn.ini

diff --git a/xtrn/bullshit/install-xtrn.ini b/xtrn/bullshit/install-xtrn.ini
new file mode 100644
index 0000000000..02fc039994
--- /dev/null
+++ b/xtrn/bullshit/install-xtrn.ini
@@ -0,0 +1,12 @@
+Name: Bullshit
+Desc: Lightbar sysop bulletin lister/reader for Synchronet
+By:   echicken -at- bbs.electronicchicken.com
+Cats: Main
+Subs: JavaScript
+Inst: 2020/09/07
+
+[prog:BULLSHIT]
+cmd  = ?bullshit.js
+settings = XTRN_MULTIUSER
+required = true
+event = EVENT_LOGON
diff --git a/xtrn/slyvote/install-xtrn.ini b/xtrn/slyvote/install-xtrn.ini
new file mode 100644
index 0000000000..749a829ad3
--- /dev/null
+++ b/xtrn/slyvote/install-xtrn.ini
@@ -0,0 +1,10 @@
+Name: SlyVote
+Desc: Voting booth for Synchronet
+By:   Nightfox (Eric Oulashin)
+Cats: Main
+Inst: 2020/09/07
+
+[prog:SLYVOTE]
+cmd  = ?slyvote
+settings = XTRN_MULTIUSER
+required = true
diff --git a/xtrn/war/install-xtrn.ini b/xtrn/war/install-xtrn.ini
new file mode 100644
index 0000000000..86777350f7
--- /dev/null
+++ b/xtrn/war/install-xtrn.ini
@@ -0,0 +1,30 @@
+; $Id: install-xtrn.ini,v 1.4 2020/04/21 04:41:22 rswindell Exp $
+; LORD installer data for install-xtrn.js
+
+Name: Solomoriah's WAR!
+Desc: WAR Version 4.4 ported to JavaScript
+By:   Chris Gonnerman, Deuce (Stephen Hurd)
+Cats: Games
+Subs: Adventure, Classic, Multiplayer, JavaScript, Port
+Inst: 2020/09/07
+
+[prog:WAR]
+cmd  = ?war.js
+ars  = NOT GUEST
+settings = XTRN_MULTIUSER
+required = true
+
+[event:WARUPD]
+prompt = false
+cmd  = ?warupd.js
+name = WAR Update
+node_num = 1
+days = 127
+
+[event:WARPOLL]
+prompt = false
+cmd  = ?warpoll.js
+name = WAR Poll
+node_num = 1
+days = 127
+freq = 60
diff --git a/xtrn/wordem/install-xtrn.ini b/xtrn/wordem/install-xtrn.ini
new file mode 100644
index 0000000000..9d6f38356a
--- /dev/null
+++ b/xtrn/wordem/install-xtrn.ini
@@ -0,0 +1,17 @@
+Name: Wordem
+Desc: InterBBS Scrabble for Synchronet
+By:   echicken -at- bbs.electronicchicken.com
+Cats: Games
+Subs: Classic, Puzzle, Words, Clone, JavaScript
+Inst: 2020/09/07
+
+[prog:WORDEM]
+cmd  = ?wordem.js
+settings = XTRN_MULTIUSER
+required = true
+
+!include install-json-service.ini
+
+[ini:json-service.ini:wordem]
+keys=dir
+values=startup_dir
\ No newline at end of file
-- 
GitLab


From 317fa56ffee22f2909a7fa4cb285d261535fe332 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 13:47:15 -0700
Subject: [PATCH 607/752] Exclude an external from the list by setting
 'xtrn-setup=false' in the install-xtrn.ini file. Used for
 known-broken/incomplete installer files.

---
 exec/xtrn-setup.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/exec/xtrn-setup.js b/exec/xtrn-setup.js
index ca6abf3e90..844907d669 100644
--- a/exec/xtrn-setup.js
+++ b/exec/xtrn-setup.js
@@ -48,6 +48,8 @@ directory(system.exec_dir + '../xtrn/*', GLOB_ONLYDIR).forEach(function (e) {
 	}
     const xtrn = f.iniGetObject();
     f.close();
+	if(xtrn['xtrn-setup'] === false)
+		return;
 	if(!xtrn.Name) {
 		alert("Skipping file with no 'Name' value: " + f.name);
 		return;
-- 
GitLab


From bce7e2af7ce0e7c7e4480e36e2c83df2f9f16e67 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 19:03:26 -0700
Subject: [PATCH 608/752] Eliminate unused variable.

---
 src/sbbs3/putmsg.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp
index b0cad61d0b..ad8c67cb08 100644
--- a/src/sbbs3/putmsg.cpp
+++ b/src/sbbs3/putmsg.cpp
@@ -53,7 +53,6 @@
 char sbbs_t::putmsg(const char *buf, long mode, long org_cols, JSObject* obj)
 {
 	uint 	tmpatr;
-	uchar	exatr=0;
 	ulong 	orgcon=console;
 	ulong	sys_status_sav=sys_status;
 	enum output_rate output_rate = cur_output_rate;
@@ -64,7 +63,7 @@ char sbbs_t::putmsg(const char *buf, long mode, long org_cols, JSObject* obj)
 		attr(LIGHTGRAY);
 	if(mode&P_NOPAUSE)
 		sys_status|=SS_PAUSEOFF;
-	
+
 	char ret = putmsgfrag(buf, &mode, org_cols, obj);
 	if(!(mode&P_SAVEATR)) {
  		console=orgcon;
-- 
GitLab


From 63c576f064a78ea3e5f5f5f32fce9be41b0de63f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 19:25:21 -0700
Subject: [PATCH 609/752] Ignore PabloDraw backup files (*.0??.*).

---
 text/.gitignore | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 text/.gitignore

diff --git a/text/.gitignore b/text/.gitignore
new file mode 100644
index 0000000000..6b2e2808db
--- /dev/null
+++ b/text/.gitignore
@@ -0,0 +1 @@
+*.0??.*
-- 
GitLab


From 8567becafcf298f136c4185e843d8f26bc25cf51 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 19:26:20 -0700
Subject: [PATCH 610/752] Mousify the control keys on the bottom of the
 main/transfer menus.

---
 text/menu/tail.asc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/text/menu/tail.asc b/text/menu/tail.asc
index 3abbb8b4f4..48a369fc9e 100644
--- a/text/menu/tail.asc
+++ b/text/menu/tail.asc
@@ -1 +1 @@
-n h4y Anytime c� hCtrl-U n4cWho's online hCtrl-P n4cSend private msg hCtrl-C n4cAbort cmd/text n
+n h4y Anytime c� h@~Ctrl-U~\x15@ n4cWho's online h@~Ctrl-P~\x10@ n4cSend private msg h@~Ctrl-C~\x03@ n4cAbort cmd/text n
-- 
GitLab


From 8d4dba9a77aab2349edd70ca03f5cded35988a38 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:07:27 -0700
Subject: [PATCH 611/752] Create the node.dab in lowercase, just in case.

---
 src/sbbs3/ctrl/NodeFormUnit.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/ctrl/NodeFormUnit.cpp b/src/sbbs3/ctrl/NodeFormUnit.cpp
index 050b230bb0..1181b8d80c 100644
--- a/src/sbbs3/ctrl/NodeFormUnit.cpp
+++ b/src/sbbs3/ctrl/NodeFormUnit.cpp
@@ -138,11 +138,11 @@ void __fastcall TNodeForm::TimerTick(TObject *Sender)
 
     if(nodedab<1) {
     	char path[MAX_PATH+1];
-        sprintf(path,"%sNODE.DAB",MainForm->global.ctrl_dir);
+        sprintf(path,"%snode.dab",MainForm->global.ctrl_dir);
     	nodedab=_sopen(path,O_RDONLY|O_BINARY|O_CREAT, SH_DENYNONE, S_IREAD|S_IWRITE);
 		if(nodedab==-1) {
 		    ListBox->Items->Clear();
-        	ListBox->Items->Add("Error "+AnsiString(errno)+" opening NODE.DAB");
+			ListBox->Items->Add("!Error "+AnsiString(errno)+" opening node.dab");
             return;
         }
     }
-- 
GitLab


From 31d59578e9918aec3719ea987e3f254152adb3fe Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:11:53 -0700
Subject: [PATCH 612/752] HTML (ZuulTerm) hasn't been supported for 2 years.
 Remove the HTML menu files.

---
 text/menu/e-mail.html   | 22 ------------
 text/menu/main.html     | 75 -----------------------------------------
 text/menu/maincfg.html  | 18 ----------
 text/menu/maininfo.html | 18 ----------
 text/menu/transfer.html | 68 -------------------------------------
 text/menu/xfercfg.html  | 15 ---------
 text/menu/xferinfo.html | 16 ---------
 7 files changed, 232 deletions(-)
 delete mode 100644 text/menu/e-mail.html
 delete mode 100644 text/menu/main.html
 delete mode 100644 text/menu/maincfg.html
 delete mode 100644 text/menu/maininfo.html
 delete mode 100644 text/menu/transfer.html
 delete mode 100644 text/menu/xfercfg.html
 delete mode 100644 text/menu/xferinfo.html

diff --git a/text/menu/e-mail.html b/text/menu/e-mail.html
deleted file mode 100644
index 807f9fffad..0000000000
--- a/text/menu/e-mail.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-<head><title>E-Mail menu</title></head>
-<body>
-<table>
- <tr>
-  <td><a href="S">E-mail the SysOp</a></td>
-  <td><a href="U">E-mail a user</a></td>
- </tr>
- <tr>
-  <td><a href="R">Read your email</a></td>
-  <td><a href="F">Send feedback</a></td>
- </tr>
- <tr>
-  <td><a href="N">Send NetMail</a></td>
-  <td><a href="K">Read email you have send</a></td>
- </tr>
- <tr>
-  <td colspan="2"><a href="Q">Return to main menu</a></td>
- </tr>
-</table>
-</body>
-</html>
diff --git a/text/menu/main.html b/text/menu/main.html
deleted file mode 100644
index 76641541eb..0000000000
--- a/text/menu/main.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<html>
-<head><title>Main Menu</title></head>
-<body>
-<table>
- <tr>
-  <td> <a href="-">Previous Sub</a> </td>
-  <td> <a href="+">Next Sub</a> </td>
- </tr>
- <tr>
-  <td> <a href="[">Previous Group</a> </td>
-  <td> <a href="]">Next Group</a> </td>
- </tr>
- <tr>
-  <td> <a href="A">Auto Message</a> </td>
-  <td> <a href="B">Browse/New Message Scan</a> </td>
- </tr>
- <tr>
-  <td> <a href="C">Chat Section</a> </td>
-  <td> <a href="D">User Config</a> </td>
- </tr>
- <tr>
-  <td> <a href="E">E-Mail</a> </td>
-  <td> <a href="F">Find text in messages</a> </td>
- </tr>
- <tr>
-  <td> <a href="G">Text section</a> </td>
-  <td> <a href="I">Info section</a> </td>
- </tr>
- <tr>
-  <td> <a href="J">Select a new sub</a> </td>
-  <td> <a href="/L">List nodes</a> </td>
- </tr>
- <tr>
-  <td> <a href="M">Time bank</a> </td>
-  <td> <a href="N">New Message Scan</a> </td>
- </tr>
- <tr>
-  <td> <a href="/N">All new messages scan</a> </td>
-  <td> <a href="O">Log off</a> </td>
- </tr>
- <tr>
-  <td> <a href="/O">Log off NOW</a> </td>
-  <td> <a href="P">Post a message</a> </td>
- </tr>
- <tr>
-  <td> <a href="Q">QWK Section</a> </td>
-  <td> <a href="R">Scan posts</a> </td>
- </tr>
- <tr>
-  <td> <a href="S">Scan messages to you</a> </td>
-  <td> <a href="/S">Scan messages to you more?</a> </td>
- </tr>
- <tr>
-  <td> <a href="U">List Users</a> </td>
-  <td> <a href="/U">List all users</a> </td>
- </tr>
- <tr>
-  <td> <a href="X">Externals</a> </td>
-  <td> <a href="Z">Continuous new message scan</a> </td>
- </tr>
- <tr>
-  <td> <a href="/Z">Cont. new message scan more?</a> </td>
-  <td> <a href="*">Sub scan?</a> </td>
- </tr>
- <tr>
-  <td> <a href="/*">Sub scan true?</a> </td>
-  <td> <a href="&amp;">Main config</a> </td>
- </tr>
- <tr>
-  <td> <a href="T">File transfers</a></td>
-  <td> <a href="~">Click here for an error</a> </td>
- </tr>
-</table>
-</body>
-</html>
diff --git a/text/menu/maincfg.html b/text/menu/maincfg.html
deleted file mode 100644
index 6befd177cb..0000000000
--- a/text/menu/maincfg.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<html>
-<head><title>Main configuration</title></head>
-<body>
-<table>
- <tr>
-  <td><a href="N">New Message scan</a></td>
-  <td><a href="S">To you scan</a></td>
- </tr>
- <tr>
-  <td><a href="P">Message pointers</a></td>
-  <td><a href="I">Re-init pointers</a></td>
- </tr>
- <tr>
-  <td colspan="2"><a href="Q">Return to previous menu</a></td>
- </tr>
-</table>
-</body>
-</html>
diff --git a/text/menu/maininfo.html b/text/menu/maininfo.html
deleted file mode 100644
index beed5e749d..0000000000
--- a/text/menu/maininfo.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<html>
-<head><title>Info section</title></head>
-<body>
-<table>
- <tr>
-  <td><a href="I">System information</a></td>
-  <td><a href="S">Sub info</a></td>
- </tr>
- <tr>
-  <td><a href="Y">Your info</a></td>
-  <td><a href="V">Version Info</a></td>
- </tr>
- <tr>
-  <td colspan="2"><a href="Q">Return to previous menu</a></td>
- </tr>
-</table>
-</body>
-</html>
diff --git a/text/menu/transfer.html b/text/menu/transfer.html
deleted file mode 100644
index d37cc6b0b2..0000000000
--- a/text/menu/transfer.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<html>
-<head><title>File Transfers</title></head>
-<body>
-<table>
- <tr>
-  <td><a href="-">Previous directory</a></td>
-  <td><a href="+">Next directory</a></td>
- </tr>
- <tr>
-  <td><a href="[">Previous library</a></td>
-  <td><a href="]">Next library</a></td>
- </tr>
- <tr>
-  <td><a href="B">Batch menu</a></td>
-  <td><a href="C">Chat section</a></td>
- </tr>
- <tr>
-  <td><a href="D">Download files</a></td>
-  <td><a href="E">List extended file information</a></td>
- </tr>
- <tr>
-  <td><a href="F">Find text in descriptions</a></td>
-  <td><a href="I">File information</a></td>
- </tr>
- <tr>
-  <td><a href="J">Change dir/lib</a></td>
-  <td><a href="L">List files</a></td>
- </tr>
- <tr>
-  <td><a href="N">New file scan</a></td>
-  <td><a href="O">Log off</a></td>
- </tr>
- <tr>
-  <td><a href="R">Remove/Edit files</a></td>
-  <td><a href="S">Search for filename</a></td>
- </tr>
- <tr>
-  <td><a href="T">File transfers</a></td>
-  <td><a href="U">Upload file</a></td>
- </tr>
- <tr>
-  <td><a href="V">View files</a></td>
-  <td><a href="Z">Upload to SysOp</a></td>
- </tr>
- <tr>
-  <td><a href="*">Change dir/lib</a></td>
-  <td><a href="/D">Download user files</a></td>
- </tr>
- <tr>
-  <td><a href="/F">Find in all</a></td>
-  <td><a href="/L">List all</a></td>
- </tr>
- <tr>
-  <td><a href="/N">New scan all</a></td>
-  <td><a href="/O">Log off NOW</a></td>
- </tr>
- <tr>
-  <td><a href="/S">Scan all</a></td>
-  <td><a href="/U">Upload to user</a></td>
- </tr>
- <tr>
-  <td><a href="/*">List libs</a></td>
-  <td><a href="&amp;">Transfer confi</a></td>
- </tr>
-</table>
-<center><a href="Q">Return to previous menu</a></center>
-</body>
-</html>
diff --git a/text/menu/xfercfg.html b/text/menu/xfercfg.html
deleted file mode 100644
index fd3fb73fa6..0000000000
--- a/text/menu/xfercfg.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<html>
-<head><title>File Transfer configuration</title></head>
-<body>
-<table>
- <tr>
-  <td><a href="P">Set newscan time</a></td>
-  <td><a href="B">Toggle batch</a></td>
- </tr>
- <tr>
-  <td><a href="E">Toggle extended descriptions</a></td>
-  <td><a href="Q">Return to previous menu</a></td>
- </tr>
-</table>
-</body>
-</html>
diff --git a/text/menu/xferinfo.html b/text/menu/xferinfo.html
deleted file mode 100644
index fcec63ad0f..0000000000
--- a/text/menu/xferinfo.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<head><title>File Info</title></head>
-<body>
-<table>
- <tr>
-  <td><a href="T">Transfer Policy</a></td>
-  <td><a href="Y">Your info</a></td>
- </tr>
- <tr>
-  <td><a href="D">Directory Info</a></td>
-  <td><a href="U">List Users</a></td>
- </tr>
-</table>
-<center><a href="Q">Return to previous menu</a></td>
-</body>
-</html>
-- 
GitLab


From 1ac4151e43f286abaadcc09cc173ee24ecccc833 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:12:49 -0700
Subject: [PATCH 613/752] Mousify the Renegade and WWIV menus.

---
 text/menu/renegade/email.asc    | 15 ++++++++-------
 text/menu/renegade/main.asc     | 25 +++++++++++++------------
 text/menu/renegade/message.asc  | 21 +++++++++++----------
 text/menu/renegade/transfer.asc | 27 ++++++++++++++-------------
 text/menu/wwiv/main.asc         |  4 ++--
 text/menu/wwiv/transfer.asc     |  4 ++--
 6 files changed, 50 insertions(+), 46 deletions(-)

diff --git a/text/menu/renegade/email.asc b/text/menu/renegade/email.asc
index 6616545401..f8a849202e 100644
--- a/text/menu/renegade/email.asc
+++ b/text/menu/renegade/email.asc
@@ -1,7 +1,8 @@
-�nhr������������������������������������������������������Ŀ
-��4         wSynchronet Reneclone Email System Menu       r0�
-���������������������������������������������������������
-  c4��������������������������������������������������������������������������Ŀ0
-  4� [yRc]ead Email       [yEc]nter Email      [yNc]etmail                          �0
-  4� [yVc]iew Outgoing    [yQc]uit to Main     [yGc]oodbye        [y/Gc]oodbye Fast!  �0
-  4����������������������������������������������������������������������������0n
+N�HR������������������������������������������������������Ŀ
+N�HR�4         WSynchronet Reneclone Email System Menu       0R�
+N�HR��������������������������������������������������������4Y@HOT@
+0R  4C��������������������������������������������������������������������������Ŀ
+0  4� [YRC]ead Email       [YEC]nter Email      [YNC]etmail                          �
+0  4� [YVC]iew Outgoing    [YQC]uit to Main     [YGC]oodbye        [Y/GC]oodbye Fast!  �
+0  4����������������������������������������������������������������������������
+Z
\ No newline at end of file
diff --git a/text/menu/renegade/main.asc b/text/menu/renegade/main.asc
index 51eca948e9..68d4a3426e 100644
--- a/text/menu/renegade/main.asc
+++ b/text/menu/renegade/main.asc
@@ -1,12 +1,13 @@
-�nhr������������������������������������������������������Ŀ
-��4         wSynchronet Reneclone Main System Menu        r0�
-���������������������������������������������������������
-  c4��������������������������������������������������������������������������Ŀ0
-  4� [yMc]essage System   [yFc]ile System      [yOc]nline System  [yEc]mail System    �0
-  4� [ySc]ystem Bulletins [yUc]ser Listing     [yYc]our Info      [yAc]uto-message    �0
-  4� [yWc]ho's online     [yLc]ist of Callers  [y!c]Offline Mail  [yPc]ersonal Info   �0
-  4� [yNc]ote to SysOp    [yCc]hat System      [yIc]nfo on System [yXc]pert Toggle    �0
-  4� [yGc]oodbye & Logoff [y/Gc]oodbye Fast!   [y$c]Time Bank     [y/Lc]ist Nodes     �0
-  4����������������������������������������������������������������������������0n
-(
-    hc[y*c] Sysop Menun)
+N�HR������������������������������������������������������Ŀ
+N�HR�4         WSynchronet Reneclone Main System Menu        0R�
+N�HR��������������������������������������������������������4Y@HOT@
+0R  4C��������������������������������������������������������������������������Ŀ
+0  4� [YMC]essage System   [YFC]ile System      [YOC]nline System  [YEC]mail System    �
+0  4� [YSC]ystem Bulletins [YUC]ser Listing     [YYC]our Info      [YAC]uto-message    �
+0  4� [YWC]ho's online     [YLC]ist of Callers  [Y!C]Offline Mail  [YPC]ersonal Info   �
+0  4� [YNC]ote to SysOp    [YCC]hat System      [YIC]nfo on System [YXC]pert Toggle    �
+0  4� [YGC]oodbye & Logoff [Y/GC]oodbye Fast!   [Y$C]Time Bank     [Y/LC]ist Nodes     �
+0  4����������������������������������������������������������������������������
+
+N�HC[Y@~*@C] Sysop Menu
+Z
\ No newline at end of file
diff --git a/text/menu/renegade/message.asc b/text/menu/renegade/message.asc
index 8fcb58d5e9..4c85314777 100644
--- a/text/menu/renegade/message.asc
+++ b/text/menu/renegade/message.asc
@@ -1,10 +1,11 @@
-�nhr������������������������������������������������������Ŀ
-��4        wSynchronet Reneclone Message System Menu      r0�
-���������������������������������������������������������
-  c4�������������������������������������������������������������������������Ŀ0
-  4� [y#c]Sub Change      [y/#c]Group Change   [y[c]Previous Sub  [y]c]Next Sub      �0
-  4� [yCc]hat System      [yRc]ead Messages    [ySc]can Messages  [yPc]ost Message   �0
-  4� [yFc]ile Menu        [yGc]oodbye          [y/Gc]oodbye       [yUc]sers w/Access �0
-  4� [yNc]ew Message Scan [yQc]uit to Main     [yEc]nter Email    [yZc]Set Scan Areas�0
-  4� [yAc]rea List        [yJc]ump to Area     [y/Lc]ist Nodes    [yYc]our Messages  �0
-  4���������������������������������������������������������������������������0n
+N�HR������������������������������������������������������Ŀ
+N�HR�4        WSynchronet Reneclone Message System Menu      0R�
+N�HR��������������������������������������������������������4Y@HOT@
+0R  4C�������������������������������������������������������������������������Ŀ
+0  4� [Y#C]Sub Change      [Y/#C]Group Change   [Y[C]Previous Sub  [Y]C]Next Sub      �
+0  4� [YCC]hat System      [YRC]ead Messages    [YSC]can Messages  [YPC]ost Message   �
+0  4� [YFC]ile Menu        [YGC]oodbye          [Y/GC]oodbye       [YUC]sers w/Access �
+0  4� [YNC]ew Message Scan [YQC]uit to Main     [YEC]nter Email    [YZC]Set Scan Areas�
+0  4� [YAC]rea List        [YJC]ump to Area     [Y/LC]ist Nodes    [YYC]our Messages  �
+0  4���������������������������������������������������������������������������
+Z
\ No newline at end of file
diff --git a/text/menu/renegade/transfer.asc b/text/menu/renegade/transfer.asc
index 1d72a4348a..113833d344 100644
--- a/text/menu/renegade/transfer.asc
+++ b/text/menu/renegade/transfer.asc
@@ -1,13 +1,14 @@
-�nhr������������������������������������������������������Ŀ
-��4         wSynchronet Reneclone File System Menu        r0�
-���������������������������������������������������������
-  c4��������������������������������������������������������������������������Ŀ0
-  4� [y#c]Dir Change      [y/#c]Lib Change     [y[c]Previous Dir  [y]c]Next Dir       �0
-  4� [y/Ac]rchive Menu    [yAc]rea List        [yCc]hat System    [yDc]ownload        �0
-  4� [yEc]nter Batch Mode [yFc]ind Description [yGc]oodbye        [y/Gc]oodbye        �0
-  4� [yPc]ointer Date     [yLc]ist Files       [yMc]essage Menu   [yNc]ew Files       �0
-  4� [yUc]pload!          [yQc]uit to Main     [yVc]iew Archive   [ySc]earch Filespec �0
-  4� [yYc]our File info   [yJc]ump to Area     [y/Lc]ist Nodes                      �0
-  4����������������������������������������������������������������������������0n
-(
-    hc[y*c] Sysop Menun)
+N�HR������������������������������������������������������Ŀ
+N�HR�4         WSynchronet Reneclone File System Menu        0R�
+N�HR��������������������������������������������������������4Y@HOT@
+0R  4C��������������������������������������������������������������������������Ŀ
+0  4� [Y#C]Dir Change      [Y/#C]Lib Change     [Y[C]Previous Dir  [Y]C]Next Dir       �
+0  4� [Y/AC]rchive Menu    [YAC]rea List        [YCC]hat System    [YDC]ownload        �
+0  4� [YEC]nter Batch Mode [YFC]ind Description [YGC]oodbye        [Y/GC]oodbye        �
+0  4� [YPC]ointer Date     [YLC]ist Files       [YMC]essage Menu   [YNC]ew Files       �
+0  4� [YUC]pload!          [YQC]uit to Main     [YVC]iew Archive   [YSC]earch Filespec �
+0  4� [YYC]our File info   [YJC]ump to Area     [Y/LC]ist Nodes                      �
+0  4����������������������������������������������������������������������������
+
+N�HC[Y@~*@C] Sysop Menu
+Z
\ No newline at end of file
diff --git a/text/menu/wwiv/main.asc b/text/menu/wwiv/main.asc
index 81f5f4e93b..834bf8cab3 100644
--- a/text/menu/wwiv/main.asc
+++ b/text/menu/wwiv/main.asc
@@ -1,2 +1,2 @@
-�nh4 WWIV Main Menu 0
-nhb������������������������������������������������������������������������������ͻ����nh4nh4 nh4Miscellaneous nhb��������nh4nh4 nh4System Features nhb�������nh4nh4 nh4Message Base Commands nhb����Ķ�nhcA  nhyAuto Message�nhb�nhcE  nhyE-Mail Menu�nhb�nhcN  nhyNew Message Scan�nhb��nhcL  nhyBBS List�nhb�nhcD  nhyDefault Section�nhb�nhcP  nhyPost Message�nhb��nhcI  nhySystem Info�nhb�nhcG  nhyText File Section   nhb�nhcQ  nhyQuick Scan Current Msg Base nhb��nhcO  nhyLog Off�nhb�nhcT  nhyTransfer Area�nhb�nhcS  nhyScan Current Message Base   nhb��nhc/O nhyFast Logoff�nhb�nhc.  nhyOn-Line Programs    nhb�nhcZ  nhyScan New Messages Non-Stop  nhb��nhcU  nhyUser List�nhb�nhcM  nhyQWK Mail Packer�nhb�nhc*  nhyList Msg Base Titles�nhb������nh4nh4 nh4Message Scansnhb���Ĵnhc&  nhyMessage Base Config nhb�nhc/* nhyList Msg Area Titles�nhb��nhcY  nhyScan for Your Msgs nhb�nhcC  nhyChat Area�nhb�nhc}  nhyMove One Msg Base Higher    nhb��nhcF  nhyFind Text in Msgs  nhb�nhcJ  nhyJump to Msg Base    nhb�nhc]  nhyMove One Msg Area Higher    nhb�����������������������Ĵnhc$  nhyTime/Credit Bank    nhb�nhc{  nhyMove One Msg Base Lower�nhb�� nhcCTRL-U  nhyWho's Online nhb�����������������������Ĵnhc[  nhyMove One Msg Area Lower�nhb�� nhcCTRL-P  nhyPage/Send Message to Another User    nhb�nhc#  nhyEnter number of Msg Base    nhb�� nhcCTRL-C  nhyAbort Command/Menu�nhb�nhc/# nhyEnter Number of Msg Area    nhb�������������������������������������������������������������������������������ͼn
\ No newline at end of file
+N�H4 WWIV Main Menu N�HC@HOT@
+B������������������������������������������������������������������������������ͻ����4W Miscellaneous 0B��������4W System Features 0B�������4W Message Base Commands 0B����Ķ�CA  YAuto MessageN�HB�CE  YE-Mail MenuN�HB�CN  YNew Message ScanN�HB��CL  YBBS ListN�HB�CD  YDefault SectionN�HB�CP  YPost MessageN�HB��CI  YSystem InfoN�HB�CG  YText File SectionN�HB�CQ  YQuick Scan Current Msg Base B��CO  YLog OffN�HB�CT  YTransfer AreaN�HB�CS  YScan Current Message BaseN�HB��C/O YFast LogoffN�HB�C.  YOn-Line ProgramsN�HB�CZ  YScan New Messages Non-Stop  B��CU  YUser ListN�HB�CM  YQWK Mail PackerN�HB�C*  YList Msg Base TitlesN�HB������4W Message Scans0B���ĴC&  YMessage Base Config B�C/* YList Msg Area TitlesN�HB��CY  YScan for Your Msgs B�CC  YChat AreaN�HB�C}  YMove One Msg Base HigherN�HB��CF  YFind Text in Msgs  B�CJ  YJump to Msg BaseN�HB�C]  YMove One Msg Area HigherN�HB�����������������������ĴC$  YTime/Credit BankN�HB�C{  YMove One Msg Base LowerN�HB�� NCTRL-UHC  YWho's Online B�����������������������ĴC[  YMove One Msg Area LowerN�HB�� NCTRL-PHC  YPage/Send Message to Another UserN�HB�C#  YEnter number of Msg BaseN�HB�� NCTRL-CHC  YAbort Command/MenuN�HB�C/# YEnter Number of Msg AreaN�HB�������������������������������������������������������������������������������ͼZ
\ No newline at end of file
diff --git a/text/menu/wwiv/transfer.asc b/text/menu/wwiv/transfer.asc
index 8e4920fc73..a7c2c5e6af 100644
--- a/text/menu/wwiv/transfer.asc
+++ b/text/menu/wwiv/transfer.asc
@@ -1,2 +1,2 @@
-�nh4 WWIV Transfer Menu 0
-ng����������ng4 nh4File Transfers ng�����������������������ng4 nh4File Searches ng��������������ķ� nhbD nhc> nhyDownload a File�ng� nhbF nhc> nhySearch by File Description�ng�� nhbR nhc> nhyRemove a File You Uploaded�ng� nhbS nhc> nhySearch by File Name�ng�� nhbU nhc> nhyUpload a File�ng� nhbL nhc> nhyList Files - Current Directory�ng�� nhbZ nhc> nhyUpload a File to Sysop�ng� nhbN nhc> nhyList all New Files�ng�� nhbE nhc> nhyExtended File Info�ng� nhb* nhc> nhyList all Directories in Library    ng�� nhbB nhc> nhyBatch Download/Upload�ng�nhb/* nhc> nhyList all Libraries�ng��nhb/D nhc> nhyDownload from user�ng� nhb# nhc> nhySelect Directory�ng��nhb/U nhc> nhyUpload to User�ng�nhb/# nhc> nhySelect Library�ng�����������ng4 nh4Miscellaneous ng������������Ķ nhb> nhc> nhyMove Forward One Directory�ng�� nhbC nhc> nhyChat Menu�ng� nhb+ nhc> nhyMove Forward One Directory�ng�� nhbO nhc> nhyLog Off�ng� nhb] nhc> nhyMove Forward One Library�ng��nhb/O nhc> nhyFast Log Off�ng� nhb< nhc> nhyMove Backward One Directory�ng�� nhbP nhc> nhySet New Search Date�ng� nhb- nhc> nhyMove Backward One Directory�ng�� nhbQ nhc> nhyQuit to Main Menu�ng� nhb[ nhc> nhyMove Backward One Library�ng�� nhbT nhc> nhySet Transfer Options�ng� nhbJ nhc> nhyJump to New File Area�ng�� nhbY nhc> nhyYour Transfer Stats�ng����������������������������������������Ķ����������ng4 nh4Archive Commands ng���������Ķ nhcCtrl U - Who's Online�ng�� nhbV nhc> nhyList Files in Archive�ng� nhcCtrl P - Send Private Message�ng�� nhbG nhc> nhyAccess Archive Command Menu�ng� nhcCtrl K - Ctrl-key Menu�ng�������������������������������������������������������������������������������Ľ
\ No newline at end of file
+N�H4 WWIV Transfer Menu N�HB@HOT@
+NG����������4 HWFile Transfers NG�����������������������4 HWFile Searches NG��������������ķ� HBD C> YDownload a FileN�G� HBF C> YSearch by File DescriptionN�G�� HBR C> YRemove a File You UploadedN�G� HBS C> YSearch by File NameN�G�� HBU C> YUpload a FileN�G� HBL C> YList Files - Current DirectoryN�G�� HBZ C> YUpload a File to SysopN�G� HBN C> YList all New FilesN�G�� HBE C> YExtended File InfoN�G� HB* C> YList all Directories in LibraryN�G�� HBB C> YBatch Download/UploadN�G�HB/* C> YList all LibrariesN�G��HB/D C> YDownload from userN�G� HB# C> YSelect DirectoryN�G��HB/U C> YUpload to UserN�G�HB/# C> YSelect LibraryN�G�����������4 HWMiscellaneous NG������������Ķ HB> C> YMove Forward One DirectoryN�G�� HBC C> YChat MenuN�G� HB+ C> YMove Forward One DirectoryN�G�� HBO C> YLog OffN�G� HB] C> YMove Forward One LibraryN�G��HB/O C> YFast Log OffN�G� HB< C> YMove Backward One DirectoryN�G�� HBP C> YSet New Search DateN�G� HB- C> YMove Backward One DirectoryN�G�� HBQ C> YQuit to Main MenuN�G� HB[ C> YMove Backward One LibraryN�G�� HBT C> YSet Transfer OptionsN�G� HBJ C> YJump to New File AreaN�G�� HBY C> YYour Transfer StatsN�G����������������������������������������Ķ����������4 HWArchive Commands NG���������Ķ HCCTRL-U - Who's OnlineN�G�� HBV C> YList Files in ArchiveN�G� HCCTRL-P - Send Private MessageN�G�� HBG C> YAccess Archive Command MenuN�G� HCCTRL-K - Ctrl-key MenuN�G�������������������������������������������������������������������������������ĽZ
\ No newline at end of file
-- 
GitLab


From 03ff6215ae417a41580f8dc15d52c0c44f9cccf4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:14:05 -0700
Subject: [PATCH 614/752] Beautify the makeguest prompt a bit (blank line
 before, one after)

---
 exec/logon.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/exec/logon.js b/exec/logon.js
index dc8158ea7f..8af08c76ce 100644
--- a/exec/logon.js
+++ b/exec/logon.js
@@ -152,11 +152,12 @@ if(user.security.level==99				/* Sysop logging on */
 	&& !system.matchuser("guest")		/* Guest account does not yet exist */
 	&& bbs.mods.userprops.get("logon", "makeguest", true) /* Sysop has not asked to stop this question */
 	) {
-	if(console.yesno("Create Guest/Anonymous user account (highly recommended)"))
+	if(console.yesno("\1?Create Guest/Anonymous user account (highly recommended)"))
 		load("makeguest.js");
-	else if(!console.yesno("Ask again later"))
+	else if(!console.yesno("Ask again later")) {
 		bbs.mods.userprops.set("logon", "makeguest", false);
-	console.crlf();
+		console.crlf();
+	}
 }
 
 // Last few callers
-- 
GitLab


From 7dcf93473ee7ae6a9b686beb3dd68a0851f5f25b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:15:37 -0700
Subject: [PATCH 615/752] If an external program section name exactly matches
 one of the categories ("Cats" list) make it a simple confirmation to choose
 it as the install location.

---
 exec/install-xtrn.js | 39 ++++++++++++++++++++++++---------------
 1 file changed, 24 insertions(+), 15 deletions(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index 14f26fdb1c..bbfaa14b0f 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -105,7 +105,7 @@ function aborted()
 	return false;
 }
 
-function install_xtrn_item(cnf, type, name, desc, item)
+function install_xtrn_item(cnf, type, name, desc, item, cats)
 {
 	if (!item.code)
 		return false;
@@ -155,20 +155,29 @@ function install_xtrn_item(cnf, type, name, desc, item)
 	if (type == "xtrn") {
 		if (!xtrn_area.sec_list.length)
 			return "No external program sections have been created";
-		
-		for (var i = 0; i < xtrn_area.sec_list.length; i++)
-			print(format("%2u: ", i + 1) + xtrn_area.sec_list[i].name);
 
-		var which;
-		while ((!which || which > xtrn_area.sec_list.length) && !aborted())
-			which = js.global.prompt("Install " + item.name  + " into which section");
-		if(aborted())
-			return false;
-		which = parseInt(which, 10);
-		if (!which)
-			return false;
-		
-		item.sec = xtrn_area.sec_list[which - 1].number;
+		for (var i = 0; i < xtrn_area.sec_list.length; i++) {
+			if(cats.indexOf(xtrn_area.sec_list[i].name) >= 0
+				&& confirm("Install " + item.name + " into " + xtrn_area.sec_list[i].name + " section")) {
+				item.sec = xtrn_area.sec_list[i].number;
+				break;
+			}
+		}
+		if(item.sec === undefined) {
+			for (var i = 0; i < xtrn_area.sec_list.length; i++)
+				print(format("%2u: ", i + 1) + xtrn_area.sec_list[i].name);
+
+			var which;
+			while ((!which || which > xtrn_area.sec_list.length) && !aborted())
+				which = js.global.prompt("Install " + item.name  + " into which section");
+			if(aborted())
+				return false;
+			which = parseInt(which, 10);
+			if (!which)
+				return false;
+
+			item.sec = xtrn_area.sec_list[which - 1].number;
+		}
 	}
 
 
@@ -247,7 +256,7 @@ function install(ini_fname)
 			var item = list[i];
 			if (item.startup_dir === undefined)
 				item.startup_dir = startup_dir;
-			var result = install_xtrn_item(xtrn_cnf, types[t].struct, name, types[t].desc, item);
+			var result = install_xtrn_item(xtrn_cnf, types[t].struct, name, types[t].desc, item, cats);
 			if (typeof result !== 'boolean')
 				return result;
 			if (result === true)
-- 
GitLab


From 19be73d51ed33b1c3c5ac6aa5b7a7c238b1fd38f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:17:17 -0700
Subject: [PATCH 616/752] We actually no longer create files in data/user/ptrs,
 so don't create.

---
 src/sbbs3/scfglib1.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 2e5c9f2be4..1f27f294f2 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -773,8 +773,6 @@ void make_data_dirs(scfg_t* cfg)
 	md(str);
 	SAFEPRINTF(str,"%suser",cfg->data_dir);
 	md(str);
-	SAFEPRINTF(str,"%suser/ptrs",cfg->data_dir);
-	md(str);
 	SAFEPRINTF(str,"%sqnet",cfg->data_dir);
 	md(str);
 	SAFEPRINTF(str,"%sfile",cfg->data_dir);
-- 
GitLab


From f29f3e13560d8ed2df1e03c038592bcfdab50d6b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:18:21 -0700
Subject: [PATCH 617/752] Exclude from xtrn-setup since this requires
 additional steps (e.g. creating a sub-board, adding filenames to the .ini). I
 would like to tackle that automation in the future through.

---
 xtrn/bullshit/install-xtrn.ini | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xtrn/bullshit/install-xtrn.ini b/xtrn/bullshit/install-xtrn.ini
index 02fc039994..f13d2e5743 100644
--- a/xtrn/bullshit/install-xtrn.ini
+++ b/xtrn/bullshit/install-xtrn.ini
@@ -2,8 +2,9 @@ Name: Bullshit
 Desc: Lightbar sysop bulletin lister/reader for Synchronet
 By:   echicken -at- bbs.electronicchicken.com
 Cats: Main
-Subs: JavaScript
+Subs: Bulletins, JavaScript
 Inst: 2020/09/07
+xtrn-setup: false
 
 [prog:BULLSHIT]
 cmd  = ?bullshit.js
-- 
GitLab


From 3dccbf338b9a7acd3f0eafd61ebad3810edc4efb Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:20:33 -0700
Subject: [PATCH 618/752] Let guest see LORD, just not play it.

---
 xtrn/lord/install-xtrn.ini | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/xtrn/lord/install-xtrn.ini b/xtrn/lord/install-xtrn.ini
index 5911cf7ea3..9e7a4694a0 100644
--- a/xtrn/lord/install-xtrn.ini
+++ b/xtrn/lord/install-xtrn.ini
@@ -1,4 +1,3 @@
-; $Id: install-xtrn.ini,v 1.4 2020/04/21 04:41:22 rswindell Exp $
 ; LORD installer data for install-xtrn.js
 
 Name: Legend of the Red Dragon
@@ -6,12 +5,11 @@ Desc: The ever-popular multi-player battle game created for Bulletin Board Syste
 By:   Seth Able Robinson, Deuce (Stephen Hurd)
 Cats: Games
 Subs: Adventure, Classic, Multiplayer, JavaScript, Port
-Inst: $Id: install-xtrn.ini,v 1.4 2020/04/21 04:41:22 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:LORD]
 cmd  = ?lord
-ars  = NOT GUEST
-execution_ars = ANSI
+execution_ars = ANSI AND NOT GUEST
 settings = XTRN_MULTIUSER
 required = true
 
-- 
GitLab


From 95acca1266cab8bbda2e73b013605f427fc27af4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:21:13 -0700
Subject: [PATCH 619/752] Let guest see LORD2, just not play it.

---
 xtrn/lord2/install-xtrn.ini | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/xtrn/lord2/install-xtrn.ini b/xtrn/lord2/install-xtrn.ini
index cdb9697ac8..61c6f83a23 100644
--- a/xtrn/lord2/install-xtrn.ini
+++ b/xtrn/lord2/install-xtrn.ini
@@ -1,4 +1,3 @@
-; $Id: install-xtrn.ini,v 1.3 2020/04/21 04:41:44 rswindell Exp $
 ; LORD II installer data for install-xtrn.js
 
 Name: LORD II: New World
@@ -6,10 +5,10 @@ Desc: Large Zelda-type world that lets players roam about as enemies or friends
 By:   Seth Able Robinson, Deuce (Stephen Hurd)
 Cats: Games
 Subs: Adventure, Classic, Multiplayer, JavaScript, Port
-Inst: $Id: install-xtrn.ini,v 1.3 2020/04/21 04:41:44 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:LORD2]
 cmd  = ?lord2
 settings = XTRN_MULTIUSER
-execution_ars = ANSI
+execution_ars = ANSI AND NOT GUEST
 required = true
-- 
GitLab


From 1814b125d1457be1979f71aa69bd943ce054725c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:21:54 -0700
Subject: [PATCH 620/752] Updated description and re-roll event.

---
 xtrn/tbd/install-xtrn.ini | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/xtrn/tbd/install-xtrn.ini b/xtrn/tbd/install-xtrn.ini
index a65b2b107e..22feeb7deb 100644
--- a/xtrn/tbd/install-xtrn.ini
+++ b/xtrn/tbd/install-xtrn.ini
@@ -1,8 +1,9 @@
 Name: The Beast's Domain
+Desc: Rogue-like, Multi-Player, Interactive, Real-Time, ANSI adventure game
 By:   King Drafus (Allen Christiansen), Domain Entertainment
 Cats: Games
-Subs: Adventure, Native
-Inst: 2020/09/06
+Subs: Adventure, Multiplayer, Native
+Inst: 2020/09/07
 
 [prog:TBD]
 cmd  = tbd%. /times=10
@@ -18,6 +19,7 @@ cmd = reroll%.
 settings = XTRN_NATIVE
 node_num = 1
 days = 127
+mdays = 2
 
 [eval:file_touch(system.data_dir + 'tbdroll.now')]
 prompt = false
-- 
GitLab


From b892aaaf676fe2a6851da15c7e33fcec15d23a47 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:23:00 -0700
Subject: [PATCH 621/752] Fixed (pass the path the "Earth" world). There are 4
 more worlds, too. Also let Guest see the game, just not play it.

---
 xtrn/war/install-xtrn.ini | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/xtrn/war/install-xtrn.ini b/xtrn/war/install-xtrn.ini
index 86777350f7..7f33768842 100644
--- a/xtrn/war/install-xtrn.ini
+++ b/xtrn/war/install-xtrn.ini
@@ -1,4 +1,3 @@
-; $Id: install-xtrn.ini,v 1.4 2020/04/21 04:41:22 rswindell Exp $
 ; LORD installer data for install-xtrn.js
 
 Name: Solomoriah's WAR!
@@ -9,21 +8,22 @@ Subs: Adventure, Classic, Multiplayer, JavaScript, Port
 Inst: 2020/09/07
 
 [prog:WAR]
-cmd  = ?war.js
-ars  = NOT GUEST
+name = Solomoriah's WAR! on Earth
+cmd  = ?war.js worlds/earth
+execution_ars  = NOT GUEST
 settings = XTRN_MULTIUSER
 required = true
 
 [event:WARUPD]
 prompt = false
-cmd  = ?warupd.js
+cmd  = ?warupd.js worlds/earth
 name = WAR Update
 node_num = 1
 days = 127
 
 [event:WARPOLL]
 prompt = false
-cmd  = ?warpoll.js
+cmd  = ?warpoll.js worlds/earth
 name = WAR Poll
 node_num = 1
 days = 127
-- 
GitLab


From e399061f8d160fd719cf431bcf532f8cdb9823a3 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 20:24:29 -0700
Subject: [PATCH 622/752] Exclude artifacts from text and xtrn directories.

---
 install/install.iss | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/install/install.iss b/install/install.iss
index 303f67b654..590e16d841 100644
--- a/install/install.iss
+++ b/install/install.iss
@@ -83,8 +83,8 @@ Source: "node1\node.cnf";                                     DestDir: "{app}\no
 Source: "node1\node.cnf";                                     DestDir: "{app}\node4"; Flags: ignoreversion
 Source: "exec\*";   DestDir: "{app}\exec";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "baja.js,menu.js,sbbsedit.js,jsdocs.js,testbuild.js,load\menulib.js"
 Source: "ctrl\*";   DestDir: "{app}\ctrl";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.?.cnf,*.?.ini"
-Source: "text\*";   DestDir: "{app}\text";  Flags: ignoreversion recursesubdirs createallsubdirs
-Source: "xtrn\*";   DestDir: "{app}\xtrn";  Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "text\*";   DestDir: "{app}\text";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.0??.*,.gitignore"
+Source: "xtrn\*";   DestDir: "{app}\xtrn";  Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.obj,*.tds,smm,.gitignore"
 Source: "docs\*";   DestDir: "{app}\docs";  Flags: ignoreversion recursesubdirs createallsubdirs
 Source: "web\*";    DestDir: "{app}\web";   Flags: ignoreversion recursesubdirs createallsubdirs
 Source: "webv4\*";                                            DestDir: "{app}\webv4";            Flags: ignoreversion recursesubdirs createallsubdirs
-- 
GitLab


From 193bf442b643d47d40727179fadcc1a1913846ab Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 23:22:38 -0700
Subject: [PATCH 623/752] Create a default server.ini file for Gooble.

---
 xtrn/gooble/server.ini | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 xtrn/gooble/server.ini

diff --git a/xtrn/gooble/server.ini b/xtrn/gooble/server.ini
new file mode 100644
index 0000000000..aa1e15cde4
--- /dev/null
+++ b/xtrn/gooble/server.ini
@@ -0,0 +1,2 @@
+host=localhost
+port=10088
-- 
GitLab


From 6a4a523ff6b9278f4c9fa0c5ee9b63c3882b3d8a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 23:23:26 -0700
Subject: [PATCH 624/752] Remove default JSON-db apps.

---
 ctrl/json-service.ini | 38 --------------------------------------
 1 file changed, 38 deletions(-)

diff --git a/ctrl/json-service.ini b/ctrl/json-service.ini
index cea4386019..c7ad39829c 100644
--- a/ctrl/json-service.ini
+++ b/ctrl/json-service.ini
@@ -8,41 +8,3 @@
 ; NOTE: after making changes to this file you MUST restart services
 ;	for the changes to take effect
 
-[boggle]
-dir=../xtrn/bublbogl/
-
-[chickendelivery2]
-dir=../xtrn/chickendelivery/
-
-[dicewarz]
-dir=../xtrn/dicewarz/
-
-[druglord]
-dir=../xtrn/druglord/
-
-[fatfish]
-dir=../xtrn/fatfish/
-
-[lemons]
-dir=../xtrn/lemons/
-
-[startrek]
-dir=../xtrn/startrek/
-
-[synchronetris]
-dir=../xtrn/synchronetris/
-
-[syncwall2]
-dir=../xtrn/syncwall/
-
-[synkroban]
-dir=../xtrn/synkroban/
-
-[thirsty]
-dir=../xtrn/thirsty/
-
-[uberblox]
-dir=../xtrn/uberblox/
-
-[tw2]
-dir=../xtrn/tw2/
\ No newline at end of file
-- 
GitLab


From 112c49d8370ea0b89aa91167f0a9d6d332a8bc8c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 23:24:24 -0700
Subject: [PATCH 625/752] Remove JSON-db and FlashPolicy services from default
 config the JSON-db service is added automatically via install-xtrn.js and
 nobody should be needing the FlashPolicy service any more.

---
 ctrl/services.ini | 14 +-------------
 1 file changed, 1 insertion(+), 13 deletions(-)

diff --git a/ctrl/services.ini b/ctrl/services.ini
index e51c9221d6..22aa6323ae 100644
--- a/ctrl/services.ini
+++ b/ctrl/services.ini
@@ -2,7 +2,7 @@
 
 ; Each section is a service, the [section name] is the service/protocol name (by default)
 
-; $Id: services.ini,v 1.14 2020/03/24 06:54:17 rswindell Exp $
+; $Id: services.ini,v 1.15 2020/09/07 rswindell $
 
 ; Global keys supported (in root section and/or per service section):
 
@@ -113,13 +113,6 @@ Port=6667
 Options=STATIC | LOOP
 Command=ircd.js
 
-; JSON inter-bbs communications server
-[JSON]
-Port=10088
-Interface=127.0.0.1
-Options=STATIC | LOOP
-Command=json-service.js
-
 [Hotline]
 Port=5500
 Command=hotline.js
@@ -143,11 +136,6 @@ Options=TLS
 Port=24553
 Command=binkit.js
 
-[FlashPolicy]
-Port=843
-Command=flashpolicyserver.js
-Enabled=false
-
 [WS]
 Port=1123
 Options=NO_HOST_LOOKUP
-- 
GitLab


From 0724fcc08b5de7bd6bc0c9fef244e9b1e39f3dfd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 23:26:16 -0700
Subject: [PATCH 626/752] Process "ini" sections before "service" sections.
 Allow a section to stop the install process by setting done=true or be the
 last of its install-type by setting last=true. These changes allow us to
 prompt whether to use remote JSON-db servers (e.g. bbs.electronicchicken.com)
 or to install/enable the JSON-db service locally, based on the sysop's
 choice.

---
 exec/install-xtrn.js | 93 +++++++++++++++++++++++++++-----------------
 1 file changed, 57 insertions(+), 36 deletions(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index bbfaa14b0f..808f73ae03 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -69,12 +69,15 @@
 // [ini:<filename.ini>[:section]]
 //      keys            = comma-separated list of keys to add/update in .ini
 //      values          = list of values to eval() and assign to keys[]
+//                        Note: string values must be enclosed in quotes!
 
 // Additionally, each section can have the following optional keys that are
 // only used by this script (i.e. not written to any configuration files):
 //		note			= note to sysop displayed before installation
 //      prompt          = confirmation prompt (or false if no prompting)
 //		required		= if true, this item must be installed to continue
+//      last            = if true, this item will be the last of its type
+//      done            = if true, no more installer items will be processed
 //
 // Notes:
 //
@@ -250,9 +253,10 @@ function install(ini_fname)
 		editor:	{ desc: "External Editor",		struct: "xedit" }
 	};
 	
+	var done = false;
 	for (var t in types) {
 		var list = ini_file.iniGetAllObjects("code", t + ":");
-		for (var i = 0; i < list.length; i++) {
+		for (var i = 0; i < list.length && !done; i++) {
 			var item = list[i];
 			if (item.startup_dir === undefined)
 				item.startup_dir = startup_dir;
@@ -263,14 +267,53 @@ function install(ini_fname)
 				installed++;
 			else if(item.required)
 				return false;
+			if(item.last === true)
+				break;
+			done = item.done;
 		}
 	}
 	
+	var list = ini_file.iniGetAllObjects("filename", "ini:");
+	for (var i = 0; i < list.length && !done; i++) {
+		var item = list[i];
+		var a = item.filename.split(':');
+		item.filename = startup_dir + a[0];
+		if(!file_exists(item.filename))
+			item.filename = file_cfgname(system.ctrl_dir, a[0]);
+		item.section = a[1] || null;
+		item.keys = item.keys.split(',');
+		item.values = item.values.split(',');
+		var prompt = "Add/update the " + (item.section || "root") + " section of " + file_getname(item.filename);
+		if (item.prompt !== undefined)
+			prompt = item.prompt;
+		if (prompt && !confirm(prompt)) {
+			if (item.required == true)
+				return prompt + " is required to continue";
+			continue;
+		}
+		var file = new File(item.filename);
+		if(!file.open(file.exists ? 'r+':'w+'))
+			return "Error " + file.error + " opening " + file.name;
+		var result = true;
+		if (options.debug)
+			print(JSON.stringify(item));
+		for(var k in item.keys) {
+			print("Setting " + item.keys[k] + " = " + eval(item.values[k]));
+			result = file.iniSetValue(item.section, item.keys[k], eval(item.values[k]));
+		}
+		file.close();
+		if(required && result !== true)
+			return false;
+		if(item.last === true)
+			break;
+		done = item.done;
+	}
+
 	var services_ini = new File(file_cfgname(system.ctrl_dir, "services.ini"));
 	var list = ini_file.iniGetAllObjects("protocol", "service:");
-	for (var i = 0; i < list.length; i++) {
+	for (var i = 0; i < list.length && !done; i++) {
 		var item = list[i];
-		var prompt = "Install/enable the " + item.protocol + " service in " + services_ini.name;
+		var prompt = "Install/enable the " + item.protocol + " service in " + file_getname(services_ini.name);
 		if (item.prompt !== undefined)
 			prompt = item.prompt;
 		if (prompt && !confirm(prompt)) {
@@ -301,41 +344,13 @@ function install(ini_fname)
 		services_ini.close();
 		if(required && result !== true)
 			return false;
-	}
-	
-	var list = ini_file.iniGetAllObjects("filename", "ini:");
-	for (var i = 0; i < list.length; i++) {
-		var item = list[i];
-		var a = item.filename.split(':');
-		item.filename = file_cfgname(system.ctrl_dir, a[0]);
-		item.section = a[1] || null;
-		item.keys = item.keys.split(',');
-		item.values = item.values.split(',');
-		var prompt = "Add/update the " + (item.section || "root") + " section of " + item.filename;
-		if (item.prompt !== undefined)
-			prompt = item.prompt;
-		if (prompt && !confirm(prompt)) {
-			if (item.required == true)
-				return prompt + " is required to continue";
-			continue;
-		}
-		var file = new File(item.filename);
-		if(!file.open(file.exists ? 'r+':'w+'))
-			return "Error " + file.error + " opening " + file.name;
-		var result = true;
-		if (options.debug)
-			print(JSON.stringify(item));
-		for(var k in item.keys) {
-			print("Setting " + item.keys[k] + " = " + eval(item.values[k]));
-			result = file.iniSetValue(item.section, item.keys[k], eval(item.values[k]));
-		}
-		file.close();
-		if(required && result !== true)
-			return false;
+		if(item.last === true)
+			break;
+		done = item.done;
 	}
 	
 	var list = ini_file.iniGetAllObjects("cmd", "exec:");
-	for (var i = 0; i < list.length; i++) {
+	for (var i = 0; i < list.length && !done; i++) {
 		var item = list[i];
 		var js_args = item.cmd.split(/\s+/);
 		var js_file = js_args.shift();
@@ -364,10 +379,13 @@ function install(ini_fname)
 			,[js_file, item.startup_dir, {}].concat(js_args));
 		if (result !== 0 && item.required)
 			return "Error " + result + " executing " + item.cmd;
+		if(item.last === true)
+			return true;
+		done = item.done;
 	}
 	
 	var list = ini_file.iniGetAllObjects("str", "eval:");
-	for (var i = 0; i < list.length; i++) {
+	for (var i = 0; i < list.length && !done; i++) {
 		var item = list[i];
 		if (!item.cmd)
 			item.cmd = item.str; // the str can't contain [], so allow cmd to override
@@ -383,6 +401,9 @@ function install(ini_fname)
 			if (item.required == true)
 				return "Truthful evaluation of '" + item.cmd + "' is required to continue";
 		}
+		if(item.last === true)
+			return true;
+		done = item.done;
 	}
 
 	if (installed) {
-- 
GitLab


From aba4ab9734ef21b1116a6fe84077026344e2fdd3 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 23:29:15 -0700
Subject: [PATCH 627/752] Allow the sysop to opt-in for sharing data with
 bbs.electronicchicken.com. If/when I get bbs.thebrokenbubble.com to respond
 to JSON-db requests (i.e. at TCP port 10088), I give mcmlxxix's games the
 same treatment. Would be nice to widely use this game data sharing feature.

---
 xtrn/ansiview/install-xtrn.ini        |  7 ++++++-
 xtrn/chickendelivery/install-xtrn.ini | 14 ++++++++++----
 xtrn/chickendelivery/server.ini       |  2 +-
 xtrn/gooble/install-xtrn.ini          |  8 +++++++-
 xtrn/startrek/install-xtrn.ini        |  6 ++++++
 xtrn/syncwall/install-xtrn.ini        | 17 ++++++++++++-----
 xtrn/thirsty/install-xtrn.ini         | 15 ++++++++++-----
 xtrn/wordem/install-xtrn.ini          |  6 ++++++
 8 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/xtrn/ansiview/install-xtrn.ini b/xtrn/ansiview/install-xtrn.ini
index 58eb5d1581..796310d474 100644
--- a/xtrn/ansiview/install-xtrn.ini
+++ b/xtrn/ansiview/install-xtrn.ini
@@ -3,8 +3,13 @@ Desc: Browse and view text files, ANSI & ASCII art
 By:   echicken -at- bbs.electronicchicken.com
 Cats: Main
 Subs: ANSI, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/25 02:38:58 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:ANSIVIEW]
 cmd  = ?ansiview
 settings = XTRN_MULTIUSER
+
+[ini:settings.ini:electronic chicken bbs]
+prompt = Link online ANSI archive from electronic chicken bbs
+keys = description,module,cache,cachettl
+values = 'An online archive of ANSI and ASCII artwork','ecbbs.js',true,86400
diff --git a/xtrn/chickendelivery/install-xtrn.ini b/xtrn/chickendelivery/install-xtrn.ini
index 4a208b21f0..a1d07ad561 100644
--- a/xtrn/chickendelivery/install-xtrn.ini
+++ b/xtrn/chickendelivery/install-xtrn.ini
@@ -3,15 +3,21 @@ Desc: You are a chicken on a mission.
 By:   echicken -at- bbs.electronicchicken.com
 Cats: Games
 Subs: Platfomer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 07:56:36 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:CHICKEN]
 cmd  = ?chickendelivery.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
+
 !include install-json-service.ini
 
-[ini:json-service.ini:chicken]
-keys=dir
-values=startup_dir
\ No newline at end of file
+[ini:json-service.ini:chickendelivery2]
+keys = dir
+values = startup_dir
\ No newline at end of file
diff --git a/xtrn/chickendelivery/server.ini b/xtrn/chickendelivery/server.ini
index 6866649958..ba4fcce5fb 100644
--- a/xtrn/chickendelivery/server.ini
+++ b/xtrn/chickendelivery/server.ini
@@ -1,3 +1,3 @@
-host=bbs.electronicchicken.com
+host=localhost
 port=10088
 autoupdate=true
diff --git a/xtrn/gooble/install-xtrn.ini b/xtrn/gooble/install-xtrn.ini
index 2a3cdad325..83c69b98bd 100644
--- a/xtrn/gooble/install-xtrn.ini
+++ b/xtrn/gooble/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: A Pac-Man knock-off for Synchronet BBS
 By:   echicken -at- bbs.electronicchicken.com
 Cats: Games
 Subs: Classic, Clone, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:09:04 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:GOOBLE]
 cmd  = ?gooble.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:gooble2]
diff --git a/xtrn/startrek/install-xtrn.ini b/xtrn/startrek/install-xtrn.ini
index d85475e03e..1e01f7a106 100644
--- a/xtrn/startrek/install-xtrn.ini
+++ b/xtrn/startrek/install-xtrn.ini
@@ -9,6 +9,12 @@ cmd  = ?startrek.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:startrek]
diff --git a/xtrn/syncwall/install-xtrn.ini b/xtrn/syncwall/install-xtrn.ini
index d837c889a5..743239cb48 100644
--- a/xtrn/syncwall/install-xtrn.ini
+++ b/xtrn/syncwall/install-xtrn.ini
@@ -1,16 +1,23 @@
 Name: SyncWall
 Desc: Inter-BBS ANSI graffiti wall for Synchronet BBS
 By:   echicken
-Cats: Communication, Messaging
+Cats: Main, Communications, Messaging
 Subs: Wall, Graffiti, InterBBS, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 09:27:15 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:SYNCWALL]
 cmd  = ?syncwall.js
 settings = XTRN_MULTIUSER
+required = true
+
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
 
 !include install-json-service.ini
 
-[ini:json-service.ini:syncwall]
-keys=dir
-values=startup_dir
\ No newline at end of file
+[ini:json-service.ini:syncwall2]
+keys = dir
+values = startup_dir
\ No newline at end of file
diff --git a/xtrn/thirsty/install-xtrn.ini b/xtrn/thirsty/install-xtrn.ini
index 046bef4947..0eead753cb 100644
--- a/xtrn/thirsty/install-xtrn.ini
+++ b/xtrn/thirsty/install-xtrn.ini
@@ -1,18 +1,23 @@
-; $Id: install-xtrn.ini,v 1.2 2020/04/17 09:23:05 rswindell Exp $
-
 Name: Thirstyville
 Desc: Inspired by the classic door game "Lemonade"
 By:   echicken -at- bbs.electronicchicken.com
 Cats: Games
 Subs: Trade, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 09:23:05 rswindell Exp $
+Inst: 2020/09/07
 
 [prog:THIRSTY]
 cmd  = ?thirsty.js
 settings = XTRN_MULTIUSER
+required = true
+
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
 
 !include install-json-service.ini
 
 [ini:json-service.ini:thirsty]
-keys=dir
-values=startup_dir
+keys = dir
+values = startup_dir
diff --git a/xtrn/wordem/install-xtrn.ini b/xtrn/wordem/install-xtrn.ini
index 9d6f38356a..edca857eb6 100644
--- a/xtrn/wordem/install-xtrn.ini
+++ b/xtrn/wordem/install-xtrn.ini
@@ -10,6 +10,12 @@ cmd  = ?wordem.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:wordem]
-- 
GitLab


From 06692ee85c1a4709c6b81be31924367ac2cc0dab Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 7 Sep 2020 23:51:25 -0700
Subject: [PATCH 628/752] This game doesn't use a server.ini file, use game.ini
 instead.

---
 xtrn/thirsty/install-xtrn.ini | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/xtrn/thirsty/install-xtrn.ini b/xtrn/thirsty/install-xtrn.ini
index 0eead753cb..891ffc2b68 100644
--- a/xtrn/thirsty/install-xtrn.ini
+++ b/xtrn/thirsty/install-xtrn.ini
@@ -10,9 +10,9 @@ cmd  = ?thirsty.js
 settings = XTRN_MULTIUSER
 required = true
 
-[ini:server.ini]
+[ini:game.ini]
 prompt = Share data with electronic chicken bbs
-keys = host
+keys = server
 values = 'bbs.electronicchicken.com'
 done = true
 
-- 
GitLab


From a2563cf0108b412edb08462e7a15d250fd4350ab Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 00:35:12 -0700
Subject: [PATCH 629/752] Make echicken's webv4 the default selection for the
 Windows installer.

---
 install/install.iss | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/install/install.iss b/install/install.iss
index 590e16d841..59e72002a5 100644
--- a/install/install.iss
+++ b/install/install.iss
@@ -58,8 +58,8 @@ Name: "enable_web"; Description: "Enable Web Server (HTTP protocol)"; GroupDescr
 Name: "enable_svcs"; Description: "Enable Synchronet Services (Finger, Gopher, NNTP, IRC, IMAP, etc.)"; GroupDescription: {#services_group}
 Name: "enable_ntsvcs"; Description: "Enable Synchronet NT services"; GroupDescription: {#ntsvcs_group}; Flags: unchecked
 Name: "enable_sexpots"; Description: "Enable POTS (dial-up modem) support service"; GroupDescription: {#ntsvcs_group}; Flags: unchecked
-name: "web_runemaster"; Description: "Enable Legacy/Runemaster Web Interface"; GroupDescription: {#webui_group}; Flags: exclusive
-name: "web_echicken"; Description: "Enable echicken's Web Interface (v4)"; GroupDescription: {#webui_group}; Flags: exclusive unchecked
+name: "web_echicken"; Description: "Enable echicken's Web Interface (v4)"; GroupDescription: {#webui_group}; Flags: exclusive
+name: "web_runemaster"; Description: "Enable Legacy/Runemaster Web Interface"; GroupDescription: {#webui_group}; Flags: unchecked exclusive
 
 [Files]
 Source: "src\sbbs3\ctrl\sbbsctrl.exe";                        DestDir: "{app}\exec";  Flags: ignoreversion
@@ -134,6 +134,7 @@ Filename: "{app}\ctrl\sbbs.ini"; Section: "FTP";      Key: "AutoStart"; String:
 Filename: "{app}\ctrl\sbbs.ini"; Section: "Web";      Key: "AutoStart"; String: "false"; Tasks: not enable_web
 Filename: "{app}\ctrl\sbbs.ini"; Section: "Services"; Key: "AutoStart"; String: "false"; Tasks: not enable_svcs
 Filename: "{app}\ctrl\sbbs.ini"; Section: "BBS";      Key: "Options"; String: "XTRN_MINIMIZED | ALLOW_RLOGIN | ALLOW_SSH | NO_DOS"; Check: IsWin64
+Filename: "{app}\ctrl\sbbs.ini"; Section: "Web";      Key: "RootDirectory"; String: "../web/root"; Tasks: web_runemaster
 Filename: "{app}\ctrl\sbbs.ini"; Section: "Web";      Key: "RootDirectory"; String: "../webv4/root"; Tasks: web_echicken
 
 [Dirs]
-- 
GitLab


From 18db62744ee94301ef73d347b150cfcc25a429bd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 00:36:13 -0700
Subject: [PATCH 630/752] Use safe string operations.

---
 src/sbbs3/ctrl/ConfigWizardUnit.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/sbbs3/ctrl/ConfigWizardUnit.cpp b/src/sbbs3/ctrl/ConfigWizardUnit.cpp
index 5d8be6bb84..c8ba1b5d6b 100644
--- a/src/sbbs3/ctrl/ConfigWizardUnit.cpp
+++ b/src/sbbs3/ctrl/ConfigWizardUnit.cpp
@@ -240,7 +240,7 @@ void __fastcall TConfigWizard::FormShow(TObject *Sender)
                 ,tz_str[i]
                 );
         else
-            strcpy(str,tz_str[i]);
+            SAFECOPY(str,tz_str[i]);
         TimeZoneComboBox->Items->Add(str);
     }
    	sprintf(str,"Other (%s)",smb_zonestr(scfg.sys_timezone,NULL));
@@ -284,13 +284,13 @@ void __fastcall TConfigWizard::NextButtonClick(TObject *Sender)
         MainForm->SaveIniSettings(Sender);
 
         // Write CNF files
-        strcpy(scfg.sys_name,SystemNameEdit->Text.c_str());
-        strcpy(scfg.sys_id,QWKIDEdit->Text.c_str());
-        strcpy(scfg.sys_location,SystemLocationEdit->Text.c_str());
-        strcpy(scfg.sys_op,SysopNameEdit->Text.c_str());
-        strcpy(scfg.sys_pass,SystemPasswordEdit->Text.c_str());
-        strcpy(scfg.sys_inetaddr,InternetAddressComboBox->Text.c_str());
-        strcpy(scfg.qnet_tagline,QNetTaglineEdit->Text.c_str());
+        SAFECOPY(scfg.sys_name,SystemNameEdit->Text.c_str());
+        SAFECOPY(scfg.sys_id,QWKIDEdit->Text.c_str());
+        SAFECOPY(scfg.sys_location,SystemLocationEdit->Text.c_str());
+        SAFECOPY(scfg.sys_op,SysopNameEdit->Text.c_str());
+        SAFECOPY(scfg.sys_pass,SystemPasswordEdit->Text.c_str());
+        SAFECOPY(scfg.sys_inetaddr,InternetAddressComboBox->Text.c_str());
+        SAFECOPY(scfg.qnet_tagline,QNetTaglineEdit->Text.c_str());
         scfg.sys_nodes=NodesUpDown->Position;
         if(TimeZoneComboBox->ItemIndex>=0
         	&& TimeZoneComboBox->ItemIndex<=sizeof(tz_val)/sizeof(tz_val[0]))
@@ -424,7 +424,7 @@ void __fastcall TConfigWizard::WizNotebookPageChanged(TObject *Sender)
                 char bbsname[41];
                 char qwkid[9];
                 int len=0;
-                strcpy(bbsname,SystemNameEdit->Text.UpperCase().c_str());
+                SAFECOPY(bbsname,SystemNameEdit->Text.UpperCase().c_str());
                 for(char*p=bbsname;*p && len<8;p++) {
                     if(strchr(ILLEGAL_QWKID_CHARS,*p))
                         continue;
-- 
GitLab


From aad0c91e933a29ee329cfe0b3e9bbd9b299a314b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 00:36:34 -0700
Subject: [PATCH 631/752] Try to fix sbbsctrl crashing after the config wizard
 on some computers

---
 src/sbbs3/ctrl/MainFormUnit.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp
index 73c9030a9c..d90862c3b9 100644
--- a/src/sbbs3/ctrl/MainFormUnit.cpp
+++ b/src/sbbs3/ctrl/MainFormUnit.cpp
@@ -3361,17 +3361,17 @@ void __fastcall TMainForm::RestoreTrayMenuItemClick(TObject *Sender)
 //---------------------------------------------------------------------------
 void __fastcall TMainForm::BBSConfigWizardMenuItemClick(TObject *Sender)
 {
-    TConfigWizard* ConfigWizard;
+    static TConfigWizard* ConfigWizard;
     static inside;
     if(inside) return;
     inside=true;
 
-    Application->CreateForm(__classid(TConfigWizard), &ConfigWizard);
+	if(ConfigWizard == NULL)
+	    Application->CreateForm(__classid(TConfigWizard), &ConfigWizard);
 	if(ConfigWizard->ShowModal()==mrOk) {
         SaveSettings(Sender);
 //        ReloadConfigExecute(Sender);  /* unnecessary since refresh_cfg() is already called */
     }
-    delete ConfigWizard;
 
     inside=false;
 }
-- 
GitLab


From 9bec12f5b1bc4bf207728e291dbb7d0cdb275d24 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 22:58:22 -0700
Subject: [PATCH 632/752] Install file.

---
 xtrn/go-for/install-xtrn.ini | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 xtrn/go-for/install-xtrn.ini

diff --git a/xtrn/go-for/install-xtrn.ini b/xtrn/go-for/install-xtrn.ini
new file mode 100644
index 0000000000..9d3c459626
--- /dev/null
+++ b/xtrn/go-for/install-xtrn.ini
@@ -0,0 +1,11 @@
+Name: Go-for
+Desc: A quickly thrown-together gopher client
+By:   echicken
+Cats: Main, Communications
+Subs: Gopher, JavaScript
+Inst: 2020/09/08
+
+[prog:GO-FOR]
+cmd  = ?go-for.js
+settings = XTRN_MULTIUSER
+required = true
-- 
GitLab


From 7c37db29b9c4d770587a431c00869f5bf5dbd5f1 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 22:58:45 -0700
Subject: [PATCH 633/752] node_num defaults to 0 (ANY), use that instead of
 node 1.

---
 xtrn/tbd/install-xtrn.ini | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/xtrn/tbd/install-xtrn.ini b/xtrn/tbd/install-xtrn.ini
index 22feeb7deb..a56a20310b 100644
--- a/xtrn/tbd/install-xtrn.ini
+++ b/xtrn/tbd/install-xtrn.ini
@@ -3,7 +3,7 @@ Desc: Rogue-like, Multi-Player, Interactive, Real-Time, ANSI adventure game
 By:   King Drafus (Allen Christiansen), Domain Entertainment
 Cats: Games
 Subs: Adventure, Multiplayer, Native
-Inst: 2020/09/07
+Inst: 2020/09/08
 
 [prog:TBD]
 cmd  = tbd%. /times=10
@@ -17,7 +17,6 @@ prompt = false
 name = TBD Re-roll
 cmd = reroll%.
 settings = XTRN_NATIVE
-node_num = 1
 days = 127
 mdays = 2
 
-- 
GitLab


From b1ce22cfca7ad96f235c77421152aed8c147d001 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 22:59:07 -0700
Subject: [PATCH 634/752] node_num defaults to 0 (ANY). Use that instead of
 node 1.

---
 xtrn/war/install-xtrn.ini | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/xtrn/war/install-xtrn.ini b/xtrn/war/install-xtrn.ini
index 7f33768842..eda488a146 100644
--- a/xtrn/war/install-xtrn.ini
+++ b/xtrn/war/install-xtrn.ini
@@ -5,7 +5,7 @@ Desc: WAR Version 4.4 ported to JavaScript
 By:   Chris Gonnerman, Deuce (Stephen Hurd)
 Cats: Games
 Subs: Adventure, Classic, Multiplayer, JavaScript, Port
-Inst: 2020/09/07
+Inst: 2020/09/08
 
 [prog:WAR]
 name = Solomoriah's WAR! on Earth
@@ -18,13 +18,11 @@ required = true
 prompt = false
 cmd  = ?warupd.js worlds/earth
 name = WAR Update
-node_num = 1
 days = 127
 
 [event:WARPOLL]
 prompt = false
 cmd  = ?warpoll.js worlds/earth
 name = WAR Poll
-node_num = 1
 days = 127
 freq = 60
-- 
GitLab


From e39d9fd7c83b2d7cd08f25b46889417289c9542e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 22:59:50 -0700
Subject: [PATCH 635/752] Set the ansiview main install item as required.

---
 xtrn/ansiview/install-xtrn.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xtrn/ansiview/install-xtrn.ini b/xtrn/ansiview/install-xtrn.ini
index 796310d474..cf8b88760a 100644
--- a/xtrn/ansiview/install-xtrn.ini
+++ b/xtrn/ansiview/install-xtrn.ini
@@ -8,6 +8,7 @@ Inst: 2020/09/07
 [prog:ANSIVIEW]
 cmd  = ?ansiview
 settings = XTRN_MULTIUSER
+required = true
 
 [ini:settings.ini:electronic chicken bbs]
 prompt = Link online ANSI archive from electronic chicken bbs
-- 
GitLab


From 8449db02e1080a4d18717e021661e336856f955d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 23:00:28 -0700
Subject: [PATCH 636/752] Catch and report exceptions thrown when calling
 eval() on .ini values.

---
 exec/install-xtrn.js | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index 808f73ae03..6080c336d2 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -298,8 +298,13 @@ function install(ini_fname)
 		if (options.debug)
 			print(JSON.stringify(item));
 		for(var k in item.keys) {
-			print("Setting " + item.keys[k] + " = " + eval(item.values[k]));
-			result = file.iniSetValue(item.section, item.keys[k], eval(item.values[k]));
+			try {
+				var value = eval(item.values[k]);
+			} catch(e) {
+				return e;
+			}
+			print("Setting " + item.keys[k] + " = " + value);
+			result = file.iniSetValue(item.section, item.keys[k], value);
 		}
 		file.close();
 		if(required && result !== true)
@@ -397,7 +402,12 @@ function install(ini_fname)
 				return prompt + " is required to continue";
 			continue;
 		}
-		if (!eval(item.cmd)) {
+		try {
+			var result = eval(item.cmd);
+		} catch(e) {
+			return e;
+		}
+		if (!result) {
 			if (item.required == true)
 				return "Truthful evaluation of '" + item.cmd + "' is required to continue";
 		}
-- 
GitLab


From 082afb7172e0ed3a16dd130bd6e730ee644bae05 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 23:01:04 -0700
Subject: [PATCH 637/752] 22 DOVE-Net sub-boards (including SYNCDATA) are
 normally only visible when run via JSexec.

---
 exec/chksetup.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/exec/chksetup.js b/exec/chksetup.js
index d30cc31822..4f4c28924d 100644
--- a/exec/chksetup.js
+++ b/exec/chksetup.js
@@ -3,7 +3,7 @@
 // Sanity-check a Synchronet BBS installation
 
 "use strict";
-const REVISION = "$Revision: 1.17 $".split(' ')[1];
+const REVISION = "$Revision: 1.18 $".split(' ')[1];
 require("sbbsdefs.js", 'USER_DELETED');
 
 function check_codes(desc, grp_list, sub_list)
@@ -257,7 +257,7 @@ var tests = {
 	
 	check_dove_net: function(options)
 	{
-		const TOTAL_DOVENET_CONFERENCES = 22;
+		const TOTAL_DOVENET_CONFERENCES = js.global.jsexec_revision ? 22 : 21;
 		var output = [];
 		var grp = msg_area.grp["DOVE-Net"];
 		if(!grp)
-- 
GitLab


From 8f0d513a05615d489d6112aae99c55b55baf108d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 23:01:53 -0700
Subject: [PATCH 638/752] Add the OutboundInterface key (blank).

---
 ctrl/sbbs.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ctrl/sbbs.ini b/ctrl/sbbs.ini
index b652aaf051..34fc04c966 100644
--- a/ctrl/sbbs.ini
+++ b/ctrl/sbbs.ini
@@ -12,6 +12,8 @@
 	Hostname =
 ; IP address of network interface to bind to (defaults to ANY/ALL interfaces):
 	Interface =
+; IPv4 address of network interface to use for outgoing connections
+	OutboundInterface =
 ; Override the SBBSCTRL environment variable (optional):
 	CtrlDirectory =
 ; Override the default system "temp" directory (optional):
-- 
GitLab


From 27290c8091490d27f1a92d9a8a6e23fe1caeff62 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 23:03:11 -0700
Subject: [PATCH 639/752] Explicitly set the default System->Loadable Modules
 for: msglist, nodelist, who's online, logonlist, and privatemsg.

---
 ctrl/main.cnf | Bin 6633 -> 6633 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/ctrl/main.cnf b/ctrl/main.cnf
index 4188c7f9f9f3288ea768d8751a5ab86606c63e0e..2f674106bd629240d4534cd8fff06147e645b900 100644
GIT binary patch
delta 46
zcmaE9{L*;C9Tw5t;`E%%;u3}2#LOH8-GZXjvdq--$%ag;nRAQNC*NbU-h7LdUjzWd
Cml72K

delta 20
ccmaE9{L*;C9hS)sOiL#_Fhy^^!^$fH0AqLvC;$Ke

-- 
GitLab


From 73d5a3237d16397f89085faf2c284931329fa66a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 8 Sep 2020 23:40:55 -0700
Subject: [PATCH 640/752] A spinning cursor would cause the screen to scroll.

Call getkey() with K_NOSPIN mode flag.
---
 exec/ftn-setup.js  | 2 +-
 exec/xtrn-setup.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/exec/ftn-setup.js b/exec/ftn-setup.js
index d52a17a464..ffef89cfb1 100644
--- a/exec/ftn-setup.js
+++ b/exec/ftn-setup.js
@@ -74,7 +74,7 @@ var key;
 var zone;
 console.ungetstr(KEY_UP);
 while (!js.terminated) {
-    key = mouse_getkey(K_NONE, undefined, true);
+    key = mouse_getkey(K_NOSPIN, undefined, true);
     //key = console.getkey();
     if (key.key.toLowerCase() == 'q') break;
 	if (key.mouse && key.mouse.press && key.mouse.button == 0 && key.mouse.y == frame.y + frame.height - 1 && key.mouse.x >= 52 && key.mouse.x <= 65) break;
diff --git a/exec/xtrn-setup.js b/exec/xtrn-setup.js
index 844907d669..f8f04006ce 100644
--- a/exec/xtrn-setup.js
+++ b/exec/xtrn-setup.js
@@ -78,7 +78,7 @@ var key;
 var xtrn;
 console.ungetstr(KEY_UP);
 while (!js.terminated) {
-    key = mouse_getkey(K_NONE, undefined, true);//console.getkey();
+    key = mouse_getkey(K_NOSPIN, undefined, true);//console.getkey();
     if (key.key.toLowerCase() == 'q') break;
     if (key.mouse && key.mouse.press && key.mouse.button == 0 && key.mouse.y == frame.y + frame.height - 1 && key.mouse.x >= 52 && key.mouse.x <= 65) break;
     t = tree.getcmd(key);
-- 
GitLab


From 1a95772ded83c3152a04f8d5f675c1e463010a80 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 00:49:01 -0700
Subject: [PATCH 641/752] Don't require a key-press when exiting a UIFC app run
 in ANSI mode on Win32

Breaking into a debugger, I found the task was hung in:
_lock_file(stdin) called from common_flush_all(), so the stream was locked.
Simply unlocking it resolves the problem.

This bug has been around forever and was annoying but non-serious (just
required the user/sysop to hit ENTER). So happy it's now fixed.
---
 src/conio/ansi_cio.c | 8 ++++++++
 src/conio/ansi_cio.h | 1 +
 src/conio/ciolib.c   | 1 +
 3 files changed, 10 insertions(+)

diff --git a/src/conio/ansi_cio.c b/src/conio/ansi_cio.c
index 5a14e11f45..5f87ec5b9b 100644
--- a/src/conio/ansi_cio.c
+++ b/src/conio/ansi_cio.c
@@ -999,6 +999,14 @@ int ansi_initciolib(long inmode)
 	return(1);
 }
 
+void ansi_suspend(void)
+{
+#if defined _WIN32
+	// Prevents the wait for a key press when exit() is called and the stdin stream is flushed
+	_unlock_file(stdin);
+#endif
+}
+
 CIOLIBEXPORT void CIOLIBCALL ansi_ciolib_setdoorway(int enable)
 {
 	if(cio_api.mode!=CIOLIB_MODE_ANSI)
diff --git a/src/conio/ansi_cio.h b/src/conio/ansi_cio.h
index 4da258d908..1a652533a0 100644
--- a/src/conio/ansi_cio.h
+++ b/src/conio/ansi_cio.h
@@ -70,6 +70,7 @@ int ansi_getch(void);
 int ansi_getche(void);
 void ansi_beep(void);
 void ansi_textmode(int mode);
+void ansi_suspend(void);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/conio/ciolib.c b/src/conio/ciolib.c
index 39d9a1fa0c..c35658bfc7 100644
--- a/src/conio/ciolib.c
+++ b/src/conio/ciolib.c
@@ -322,6 +322,7 @@ static int try_ansi_init(int mode)
 		cio_api.textmode=ansi_textmode;
 		cio_api.ESCDELAY=&CIOLIB_ANSI_TIMEOUT;
 		cio_api.beep=ansi_beep;
+		cio_api.suspend=ansi_suspend;
 		return(1);
 	}
 	return(0);
-- 
GitLab


From e542c523ac2625e6790f4b8dd627a139fe62ddbe Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 00:55:17 -0700
Subject: [PATCH 642/752] Honor the "pause after exit" setting for external
 programs.

---
 exec/lbshell.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/exec/lbshell.js b/exec/lbshell.js
index a8a7717735..26be2a01bc 100644
--- a/exec/lbshell.js
+++ b/exec/lbshell.js
@@ -708,6 +708,8 @@ while(bbs.online) {
 					}
 					else {
 						bbs.exec_xtrn(xtrn_area.sec_list[curr_xtrnsec].prog_list[parseInt(x_prog)].number);
+						if(xtrn_area.sec_list[curr_xtrnsec].prog_list[parseInt(x_prog)].settings & XTRN_PAUSE)
+							console.pause();
 					}
 					start_mouse();
 					draw_main(true);
-- 
GitLab


From a0d1e8d1d85c4ab3499cac118193223cd801a1b1 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 02:14:48 -0700
Subject: [PATCH 643/752] Spinning cursor over the hardware cursor - disabled.
 Define SPINNING_CURSOR_OVER_HARDWARE_CURSOR to enable this experimental
 feature. I'm not sure I like it better than the way the spinning cursor has
 been displayed for decades (to the left of the hardware cursor).

---
 src/sbbs3/getkey.cpp | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/getkey.cpp b/src/sbbs3/getkey.cpp
index ab02f3c267..f4a0258cca 100644
--- a/src/sbbs3/getkey.cpp
+++ b/src/sbbs3/getkey.cpp
@@ -61,19 +61,30 @@ char sbbs_t::getkey(long mode)
 		mode|=(useron.misc&SPIN);
 	lncntr=0;
 	timeout=time(NULL);
+#if !defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
 	if(mode&K_SPIN)
 		outchar(' ');
-
+#endif
 	do {
 		if(sys_status&SS_ABORT) {
-			if(mode&K_SPIN) /* back space once if on spinning cursor */
+			if(mode&K_SPIN) {
+#if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
+				bputs(" \b");
+#else
 				backspace();
+#endif
+			}
 			return(0); 
 		}
 
 		if(mode&K_SPIN) {
+#if !defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
 			outchar('\b');
+#endif
 			outchar(cursor[(c++) % cursors]);
+#if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
+			outchar('\b');
+#endif
 		}
 		ch=inkey(mode,mode&K_SPIN ? 200:1000);
 		if(sys_status&SS_ABORT)
@@ -86,8 +97,13 @@ char sbbs_t::getkey(long mode)
 				continue;
 			if(mode&K_NOEXASC && ch&0x80)
 				continue;
-			if(mode&K_SPIN)
+			if(mode&K_SPIN) {
+#if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
+				bputs(" \b");
+#else
 				backspace();
+#endif
+			}
 			if(mode&K_COLD && ch>' ' && useron.misc&COLDKEYS) {
 				if(mode&K_UPPER)
 					outchar(toupper(ch));
-- 
GitLab


From ee44592aa74c801f75908c218272fbc2a314d7f5 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 02:16:47 -0700
Subject: [PATCH 644/752] Improve file copy (fcopy()) performance by more than
 an order of magnitude. Using a 256KB read buffer for copying files (rather
 than one byte at a time). Apparently calling fread() is not the same as a
 bunch of calls to fgetc() after all. Or maybe it was the many calls to
 fputc() being replaced with fwrite(). Or maybe it was both. Anyway, decreased
 the time to copy a 1GB file from and to a Samba share over a Gb Ethernet
 network from 13 minutes to less than a minute. This matters when sbbs is
 backing up your data/mail base and the files are big. The mail base is locked
 while being backed up and the longer it takes to back up, the longer the mail
 base is locked and no mail can be received, read or sent during that time.

---
 src/sbbs3/nopen.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/sbbs3/nopen.c b/src/sbbs3/nopen.c
index cbeb653e20..206d79cb64 100644
--- a/src/sbbs3/nopen.c
+++ b/src/sbbs3/nopen.c
@@ -150,7 +150,7 @@ BOOL fmutex(const char* fname, const char* text, long max_age)
 
 BOOL fcopy(const char* src, const char* dest)
 {
-	int		ch;
+	uint8_t	buf[256 * 1024];
 	ulong	count=0;
 	FILE*	in;
 	FILE*	out;
@@ -164,14 +164,14 @@ BOOL fcopy(const char* src, const char* dest)
 	}
 
 	while(!feof(in)) {
-		if((ch=fgetc(in))==EOF)
+		size_t rd = fread(buf, sizeof(uint8_t), sizeof(buf), in);
+		if(rd < 1)
 			break;
-		if(fputc(ch,out)==EOF) {
-			success=FALSE;
+		if(fwrite(buf, sizeof(uint8_t), rd, out) != rd) {
+			success = FALSE;
 			break;
 		}
-		if(((count++)%(32*1024))==0)
-			MAYBE_YIELD();
+		MAYBE_YIELD();
 	}
 
 	fclose(in);
-- 
GitLab


From e4dee8f869006e76b76cd013a2ee80ac8834e174 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 10:40:00 -0700
Subject: [PATCH 645/752] Added special finger request: json-dbs

Returns a list of publicly accessible JSON db's from ctrl/json-service.ini
(assumes the JSON service is not just listening on localhost).
Sysop can opt-out by setting json_db=false in the [fingerservice]
section of modopts.ini

Now mcmlxxix has a reason to expose his BBS to the world's fingers.
---
 exec/fingerservice.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/exec/fingerservice.js b/exec/fingerservice.js
index 8c90539f43..125b1f6ac0 100644
--- a/exec/fingerservice.js
+++ b/exec/fingerservice.js
@@ -62,6 +62,8 @@ if(options.findfile === undefined)
 	options.findfile = false;
 if(options.bbslist === undefined)
 	options.bbslist = false;
+if(options.json_dbs === undefined)
+	options.json_dbs = true;
 
 load("nodedefs.js");
 load("sockdefs.js");
@@ -382,6 +384,18 @@ if(request.charAt(0)=='?' || request.charAt(0)=='.') {	// Handle "special" reque
 			}
 			break;
 
+		case "json-dbs":
+			var f = new File(system.ctrl_dir + "json-service.ini");
+			if(f.open("r")) {
+				var obj = f.iniGetAllObjects();
+				for(var i in obj) {
+					if(!obj[i].read && !obj[i].write)
+						writeln(obj[i].name);
+				}
+				f.close();
+			}
+			break;
+
 		default:
 			if(options.bbslist && request.indexOf("bbs:") == 0) {
 				var list = sbbslist.read_list();
@@ -406,6 +420,8 @@ if(request.charAt(0)=='?' || request.charAt(0)=='.') {	// Handle "special" reque
 			writeln("\tsockopts");
 			writeln("\tnodelist");
 			writeln("\tactive-users.json");
+			if(options.json_dbs)
+				writeln("\tjson-dbs");
 			if(options.findfile)
 				writeln("\tfindfile");
 			if(options.bbslist) {
-- 
GitLab


From 2b892eae1f847fe845f24c2f1ad34dfd929cb0dd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 14:06:19 -0700
Subject: [PATCH 646/752] Replace fcopy with CopyFile (Win32 API function) on
 Windows. Thanks to Wilfred van Velzen (2:280/464) for the tip. CopyFile()
 reduced the 1GB file copy test (from and to CIFS/SMB) duration from 37
 seconds to 5 seconds with very low CPU utilization! Created a wrapper for
 non-Windows OSes in xpdev/dirwrap. This is where non-Windows-OS-specific
 optimized versions may appear in the future, but for now it's just the
 previous fcopy() implementation (using a 256KB stack buffer). sbbs doesn't
 actually copy files very often, so this isn't as big of a deal as one might
 assume. The JavaScript global method: file_copy() also benefits from these
 improvements, so any scripts that use it (e.g. tickit.js) will also benefit.
 binkit.js has its own file-copy logic (using a 2MB buffer), so no change
 there.

---
 src/sbbs3/js_global.c  |  2 +-
 src/sbbs3/nopen.c      | 34 +---------------------------------
 src/sbbs3/nopen.h      |  1 -
 src/sbbs3/writemsg.cpp |  2 +-
 src/xpdev/dirwrap.c    | 36 ++++++++++++++++++++++++++++++++++++
 src/xpdev/dirwrap.h    |  4 ++++
 6 files changed, 43 insertions(+), 36 deletions(-)

diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c
index 5950ecd173..3463c010f6 100644
--- a/src/sbbs3/js_global.c
+++ b/src/sbbs3/js_global.c
@@ -3047,7 +3047,7 @@ js_fcopy(JSContext *cx, uintN argc, jsval *arglist)
 	}
 
 	rc=JS_SUSPENDREQUEST(cx);
-	ret=fcopy(src,dest);
+	ret = CopyFile(src, dest, /* failIfExists: */FALSE);
 	free(src);
 	free(dest);
 	JS_RESUMEREQUEST(cx, rc);
diff --git a/src/sbbs3/nopen.c b/src/sbbs3/nopen.c
index 206d79cb64..b712188b0e 100644
--- a/src/sbbs3/nopen.c
+++ b/src/sbbs3/nopen.c
@@ -148,38 +148,6 @@ BOOL fmutex(const char* fname, const char* text, long max_age)
 	return TRUE;
 }
 
-BOOL fcopy(const char* src, const char* dest)
-{
-	uint8_t	buf[256 * 1024];
-	ulong	count=0;
-	FILE*	in;
-	FILE*	out;
-	BOOL	success=TRUE;
-
-	if((in=fopen(src,"rb"))==NULL)
-		return FALSE;
-	if((out=fopen(dest,"wb"))==NULL) {
-		fclose(in);
-		return FALSE;
-	}
-
-	while(!feof(in)) {
-		size_t rd = fread(buf, sizeof(uint8_t), sizeof(buf), in);
-		if(rd < 1)
-			break;
-		if(fwrite(buf, sizeof(uint8_t), rd, out) != rd) {
-			success = FALSE;
-			break;
-		}
-		MAYBE_YIELD();
-	}
-
-	fclose(in);
-	fclose(out);
-
-	return(success);
-}
-
 BOOL fcompare(const char* fn1, const char* fn2)
 {
 	FILE*	fp1;
@@ -240,7 +208,7 @@ BOOL backup(const char *fname, int backup_level, BOOL ren)
 				/* preserve the original time stamp */
 				ut.modtime = fdate(fname);
 
-				if(!fcopy(fname,newname))
+				if(!CopyFile(fname, newname, /* failIfExists: */FALSE))
 					return FALSE;
 
 				ut.actime = time(NULL);
diff --git a/src/sbbs3/nopen.h b/src/sbbs3/nopen.h
index d9769d0988..c03f8e6af9 100644
--- a/src/sbbs3/nopen.h
+++ b/src/sbbs3/nopen.h
@@ -47,7 +47,6 @@ int		nopen(const char* str, int access);
 FILE *	fnopen(int* file, const char* str, int access);
 BOOL	ftouch(const char* fname);
 BOOL	fmutex(const char* fname, const char* text, long max_age);
-BOOL	fcopy(const char* src, const char* dest);
 BOOL	fcompare(const char* fn1, const char* fn2);
 BOOL	backup(const char* org, int backup_level, BOOL ren);
 
diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp
index c8fcda6fff..5a31310774 100644
--- a/src/sbbs3/writemsg.cpp
+++ b/src/sbbs3/writemsg.cpp
@@ -1236,7 +1236,7 @@ bool sbbs_t::editfile(char *fname, bool msg)
 		if(stricmp(msgtmp,path)) {
 			removecase(msgtmp);
 			if(fexistcase(path))
-				fcopy(path, msgtmp);
+				CopyFile(path, msgtmp, /* failIfExists: */FALSE);
 		}
 
 		editor_inf(useron_xedit,/* to: */fname,/* from: */nulstr,/* subj: */nulstr,/* mode: */0,INVALID_SUB,/* tagfile: */NULL);
diff --git a/src/xpdev/dirwrap.c b/src/xpdev/dirwrap.c
index 268aea1502..1e8214a15c 100644
--- a/src/xpdev/dirwrap.c
+++ b/src/xpdev/dirwrap.c
@@ -1218,3 +1218,39 @@ int DLLCALL mkpath(const char* path)
 
 	return(result);
 }
+
+#if !defined _WIN32
+BOOL CopyFile(const char* src, const char* dest, BOOL failIfExists)
+{
+	uint8_t	buf[256 * 1024];
+	ulong	count=0;
+	FILE*	in;
+	FILE*	out;
+	BOOL	success=TRUE;
+
+	if(failIfExists && fexist(dest))
+		return FALSE;
+	if((in=fopen(src,"rb"))==NULL)
+		return FALSE;
+	if((out=fopen(dest,"wb"))==NULL) {
+		fclose(in);
+		return FALSE;
+	}
+
+	while(!feof(in)) {
+		size_t rd = fread(buf, sizeof(uint8_t), sizeof(buf), in);
+		if(rd < 1)
+			break;
+		if(fwrite(buf, sizeof(uint8_t), rd, out) != rd) {
+			success = FALSE;
+			break;
+		}
+		MAYBE_YIELD();
+	}
+
+	fclose(in);
+	fclose(out);
+
+	return success;
+}
+#endif
diff --git a/src/xpdev/dirwrap.h b/src/xpdev/dirwrap.h
index c98f3f4f88..4bb505b22f 100644
--- a/src/xpdev/dirwrap.h
+++ b/src/xpdev/dirwrap.h
@@ -250,6 +250,10 @@ DLLEXPORT int DLLCALL removecase(const char *path);
 	#define	removecase(x)	remove(x)
 #endif
 
+#if !defined _WIN32
+DLLEXPORT BOOL		CopyFile(const char* src, const char* dest, BOOL failIfExists);
+#endif
+
 #if defined(__cplusplus)
 }
 #endif
-- 
GitLab


From d44f9fd71461b61b6bb7284a5736acedecfb7d9f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 20:58:16 -0700
Subject: [PATCH 647/752] Fix problem with podcast episodes not being listed
 (e.g. techdorks.net)

A message header's from_net_type can be undefined
And apparently undefined != NET_NONE even though NET_NONE is 0.
JavaScript is weird.
---
 exec/load/podcast_routines.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/load/podcast_routines.js b/exec/load/podcast_routines.js
index cf82977440..6e92497aa6 100644
--- a/exec/load/podcast_routines.js
+++ b/exec/load/podcast_routines.js
@@ -40,7 +40,7 @@ function podcast_load_headers(base, from, to, all_hdrs)
 			continue;
 		if (hdr.from.toLowerCase() != from.toLowerCase() || hdr.to.toLowerCase() != to.toLowerCase())
 			continue;
-		if (hdr.from_net_type != NET_NONE)
+		if (hdr.from_net_type)
 			continue;
 		hdrs.push(hdr);
 	}
-- 
GitLab


From 875d783d2de1c8ae8c7fd21f4a2f3c968f1e4b89 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 21:02:27 -0700
Subject: [PATCH 648/752] Fix sbbslist.js line 1769: TypeError: bbs is
 undefined

Going backwards (negative) in the BBS list while using the 'C'
(preview capture) command would trigger this exception.

Also, don't enable spinning cursor while waiting for a key-press
after displaying a preview.
---
 exec/sbbslist.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/exec/sbbslist.js b/exec/sbbslist.js
index 8108135694..5e96ca6bca 100644
--- a/exec/sbbslist.js
+++ b/exec/sbbslist.js
@@ -1746,7 +1746,7 @@ function view(list, current)
 					}
 					console.clear();
 					lib.draw_preview(bbs);
-					key = console.getkey();
+					key = console.getkey(K_NOSPIN);
 					console.clear();
 					if(!is_nav_key(key))
 						break;
@@ -1763,10 +1763,12 @@ function view(list, current)
 								current++;
 								break;
 						}
-						if(current >= list.length)
+						if(current < 0 || current >= list.length)
 							break;
 						bbs = list[current];
 					} while(!bbs.preview);
+					if(current < 0 || current >= list.length)
+						break;
 				}
 				break;
 			case 'L':
@@ -1778,7 +1780,7 @@ function view(list, current)
 					if(result == true) {
 						console.clear();
 						lib.draw_preview(copy);
-						console.getkey();
+						console.getkey(K_NOSPIN);
 					} else {
 						console.crlf();
 						alert("Result: " + result);
-- 
GitLab


From 35a27554d4626a4d643f8ea93e6b907e2cdf5719 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 9 Sep 2020 21:15:24 -0700
Subject: [PATCH 649/752] Resolve GCC warnings.

---
 src/sbbs3/js_file_area.c | 6 +++---
 src/sbbs3/js_msg_area.c  | 4 ++--
 src/xpdev/dirwrap.c      | 1 -
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/sbbs3/js_file_area.c b/src/sbbs3/js_file_area.c
index 1a1d5f9e43..7b0bec7a63 100644
--- a/src/sbbs3/js_file_area.c
+++ b/src/sbbs3/js_file_area.c
@@ -129,7 +129,7 @@ JSBool DLLCALL js_file_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
 
 	if(id != JSID_VOID && id != JSID_EMPTY) {
 		jsval idval;
-		
+
 		JS_IdToValue(cx, id, &idval);
 		if(JSVAL_IS_STRING(idval))
 			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
@@ -219,7 +219,7 @@ JSBool DLLCALL js_file_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
 			lib_index=-1;
 			if(p->user==NULL || chk_ar(p->cfg,p->cfg->lib[l]->ar,p->user,p->client)) {
 
-				if(!JS_GetArrayLength(cx, lib_list, &lib_index))
+				if(!JS_GetArrayLength(cx, lib_list, (jsuint*)&lib_index))
 					return JS_FALSE;
 
 				if(!JS_SetElement(cx, lib_list, lib_index, &val))
@@ -291,7 +291,7 @@ JSBool DLLCALL js_file_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
 				dir_index=-1;
 				if(p->user==NULL || chk_ar(p->cfg,p->cfg->dir[d]->ar,p->user,p->client)) {
 
-					if(!JS_GetArrayLength(cx, dir_list, &dir_index))
+					if(!JS_GetArrayLength(cx, dir_list, (jsuint*)&dir_index))
 						return JS_FALSE;
 
 					if(!JS_SetElement(cx, dir_list, dir_index, &val))
diff --git a/src/sbbs3/js_msg_area.c b/src/sbbs3/js_msg_area.c
index bc8bed5a1e..c3d2d8307a 100644
--- a/src/sbbs3/js_msg_area.c
+++ b/src/sbbs3/js_msg_area.c
@@ -447,7 +447,7 @@ JSBool DLLCALL js_msg_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
 			grp_index=-1;
 			if(p->user==NULL || chk_ar(p->cfg,p->cfg->grp[l]->ar,p->user,p->client)) {
 
-				if(!JS_GetArrayLength(cx, grp_list, &grp_index))
+				if(!JS_GetArrayLength(cx, grp_list, (jsuint*)&grp_index))
 					return JS_FALSE;
 
 				if(!JS_SetElement(cx, grp_list, grp_index, &val))
@@ -538,7 +538,7 @@ JSBool DLLCALL js_msg_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
 				sub_index=-1;
 				if(p->user==NULL || can_user_access_sub(p->cfg,d,p->user,p->client)) {
 
-					if(!JS_GetArrayLength(cx, sub_list, &sub_index))
+					if(!JS_GetArrayLength(cx, sub_list, (jsuint*)&sub_index))
 						return JS_FALSE;
 
 					if(!JS_SetElement(cx, sub_list, sub_index, &val))
diff --git a/src/xpdev/dirwrap.c b/src/xpdev/dirwrap.c
index 1e8214a15c..2882ddaadf 100644
--- a/src/xpdev/dirwrap.c
+++ b/src/xpdev/dirwrap.c
@@ -1223,7 +1223,6 @@ int DLLCALL mkpath(const char* path)
 BOOL CopyFile(const char* src, const char* dest, BOOL failIfExists)
 {
 	uint8_t	buf[256 * 1024];
-	ulong	count=0;
 	FILE*	in;
 	FILE*	out;
 	BOOL	success=TRUE;
-- 
GitLab


From 69550cf864758ea7ae0715355b4f79bc912bf56c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 10 Sep 2020 01:25:04 -0700
Subject: [PATCH 650/752] The announced list of new features added in v3.17b

---
 docs/v317_new.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 docs/v317_new.txt

diff --git a/docs/v317_new.txt b/docs/v317_new.txt
new file mode 100644
index 0000000000..3f6beecd3f
--- /dev/null
+++ b/docs/v317_new.txt
@@ -0,0 +1,45 @@
+
+Subj : Synchronet v3.17b release
+To   : All
+From : Digital Man #1
+Date : Tue Jan 01 2019 02:45 pm PST
+
+Synchronet v3.17, under development since the release of v3.16c in 2015, has
+been officially released and dubbed v3.17b (no, the 'b' is not for beta).
+
+What changed (since v3.16)?
+
+- IPv6 support
+- TLS everywhere (HTTPS, FTPS, SMTPS, POP3S, NNTPS, etc.)
+- SSH server reliability and compatibility improvements
+- User avatars (across local and networked message bases)
+  http://wiki.synchro.net/module:avatars
+- Polls and message up/down voting (across local and networked message bases)
+- Integrated Fido/BinkP mailer (BinkIT.js)
+  http://wiki.synchro.net/module:binkit
+- Revamped inter-bbs instant messaging
+  http://wiki.synchro.net/module:privatemsg
+- Revamped BBS list module (sbbslist v4)
+  http://wiki.synchro.net/module:sbbslist
+- Custom/loadable/contextual terminal fonts
+  http://wiki.synchro.net/config:fonts.ini
+- Custom terminal palettes (full XBin support)
+  http://wiki.synchro.net/ref:xbin
+- CBM/PETSCII terminal support (including 40-column terminals)
+- echicken's web interface v4 included (current snapshot)
+- Numerous performance and usability improvements and bug-fixes
+
+The fresh installer for Windows is at ftp://ftp.synchro.net/sbbs317b.zip
+The upgrade package (e.g. from v3.16c) is at ftp://ftp.synchro.net/sbup317b.zip
+
+If you're already running v3.17a (development version), just update according
+to the instructions here: http://wiki.synchro.net/install:dev
+
+Many thanks to Deuce, echicken, Nelgin, and all the other active sysops, users,
+testers, contributors and providers of helpful feedback!
+
+                                            HYdigital mann
+
+Synchronet/BBS Terminology Definition #6:
+BinkD = Binkley Daemon
+Norco, CA WX: 53.4�F, 17.0% humidity, 7 mph SSE wind, 0.00 inches rain/24hrs
-- 
GitLab


From d582046d19736d8f0a847bd6ffa743d1e6bbced9 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 10 Sep 2020 14:59:19 -0700
Subject: [PATCH 651/752] Replace CP437 char (254) with C-escape sequence
 (\xfe)

---
 exec/default.src | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/exec/default.src b/exec/default.src
index 4ede06ccd7..c8774ea630 100644
--- a/exec/default.src
+++ b/exec/default.src
@@ -51,7 +51,7 @@ if_true
 	end_if
 
 # Display main Prompt
-print "\1-\1c\r\n� \1b\1hMain \1n\1c� \1h"
+print "\1-\1c\r\n\xfe \1b\1hMain \1n\1c\xfe \1h"
 compare_ars exempt T
 if_true
 	print "@TUSED@"
@@ -533,7 +533,7 @@ async
 inc_file_cmds
 
 # Display main Prompt
-print "\1-\1c\r\n� \1b\1hFile \1n\1c� \1h"
+print "\1-\1c\r\n\xfe \1b\1hFile \1n\1c\xfe \1h"
 compare_ars exempt T
 if_true
 	print "@TUSED@"
-- 
GitLab


From 9d265bec85997bb7b3da802043c02c38c4df5e51 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 10 Sep 2020 15:23:57 -0700
Subject: [PATCH 652/752] Replace control chars (e.g. ASCII 1, Ctrl-A) with
 C-escapes (e.g. \x01)

---
 exec/major.src   | 16 +++++------
 exec/matrix.src  | 36 ++++++++++++-------------
 exec/pcboard.src | 14 +++++-----
 exec/wwiv.src    | 70 ++++++++++++++++++++++++------------------------
 4 files changed, 68 insertions(+), 68 deletions(-)

diff --git a/exec/major.src b/exec/major.src
index 8a69ac35bb..0365bce932 100644
--- a/exec/major.src
+++ b/exec/major.src
@@ -53,7 +53,7 @@ cmdkey E
 		end_cmd
 
 	cmdkey W
-		print "_\r\nbhE-mail (User name or number): w"
+		print "\x01_\r\n\x01b\x01hE-mail (User name or number): \x01w"
 		getname 40
 		compare_str ""
 		if_true
@@ -83,7 +83,7 @@ cmdkey E
 		end_cmd
 
 	cmdkey U
-		print "_\r\nbhE-mail (User name or number): w"
+		print "\x01_\r\n\x01b\x01hE-mail (User name or number): \x01w"
 		getname 40
 		compare_str ""
 		if_true
@@ -141,7 +141,7 @@ cmdkey A
 	end_cmd
 
 cmdkey R
-	print "\r\nchList Users\r\n"
+	print "\r\n\x01c\x01hList Users\r\n"
 	mnemonics "\r\n~Logons Today, ~Sub-board, or ~All: "
 	getcmd LSA\r
 
@@ -236,7 +236,7 @@ cmdkey Q
 		cmd_home
 		menu maincfg
 		async
-		print "\r\nyhConfig: n"
+		print "\r\n\x01y\x01hConfig: \x01n"
 		getcmd ?QNPIS
 		logkey
 
@@ -300,7 +300,7 @@ getkey
 logkey
 
 cmdkey F
-	print "\r\nchList Files\r\n"
+	print "\r\n\x01c\x01hList Files\r\n"
 	getfilespec
 	if_true
 		file_list
@@ -308,7 +308,7 @@ cmdkey F
 	end_cmd
 
 cmdkey D
-	print "\r\nchDownload File(s)\r\n"
+	print "\r\n\x01c\x01hDownload File(s)\r\n"
 	file_download_batch
 	if_true
 		end_cmd
@@ -320,7 +320,7 @@ cmdkey D
 	end_cmd
 
 cmdkey U
-	print "\r\nchUpload File\r\n"
+	print "\r\n\x01c\x01hUpload File\r\n"
 	chkfile "%zmenu/upload.*"
 	if_true
 		menu upload
@@ -343,7 +343,7 @@ end_cmd
 
 :sysop
 menu sysmain
-print "n\r\nchSelect an option (or X to exit): n"
+print "\x01n\r\n\x01c\x01hSelect an option (or X to exit): \x01n"
 getstrupr 40
 
 compare_str "X"
diff --git a/exec/matrix.src b/exec/matrix.src
index 094111a061..491d6596ea 100644
--- a/exec/matrix.src
+++ b/exec/matrix.src
@@ -39,42 +39,42 @@ else
         end_if
 
 print "                        "
-print "4yh"
-print "������������������������������ͻn\r\n"
+print "\x014\x01y\x01h"
+print "������������������������������ͻ\x01n\r\n"
 print "                        "
-print "4yh"
-print "�         Logon Matrix         �nb��n\r\n"
+print "\x014\x01y\x01h"
+print "�         Logon Matrix         �\x01n\x01b��\x01n\r\n"
 print "                        "
-print "4yh"
-print "������������������������������͹nb��n\r\n"
-print "                        4yh� "
+print "\x014\x01y\x01h"
+print "������������������������������͹\x01n\x01b��\x01n\r\n"
+print "                        \x014\x01y\x01h� "
 compare m 0
 	if_equal
-		print "7bh"
+		print "\x10\x017\x01b\x01h"
 	else
-		print " 4wh"
+		print " \x014\x01w\x01h"
 	end_if
 print " Existing User Account     "
-print "4y �nb��n\r\n                        4yh� "
+print "\x014\x01y �\x01n\x01b��\x01n\r\n                        \x014\x01y\x01h� "
 compare m 1
 	if_equal
-		print "7bh"
+		print "\x10\x017\x01b\x01h"
 	else
-		print " 4wh"
+		print " \x014\x01w\x01h"
 	end_if
 print " New User Account          "
-print "4y �nb��n\r\n                        4yh� "
+print "\x014\x01y �\x01n\x01b��\x01n\r\n                        \x014\x01y\x01h� "
 compare m 2
 	if_equal
-		print "7bh"
+		print "\x10\x017\x01b\x01h"
 	else
-		print " 4wh"
+		print " \x014\x01w\x01h"
 	end_if
 print " Guest User Account        "
-print "4y �nb��n\r\n                        4yh"
-print "������������������������������ͼnb��n\r\nnb"
+print "\x014\x01y �\x01n\x01b��\x01n\r\n                        \x014\x01y\x01h"
+print "������������������������������ͼ\x01n\x01b��\x01n\r\n\x01n\x01b"
 print "                         "
-print " ��������������������������������n\r\n"
+print " ��������������������������������\x01n\r\n"
 
 #sync
 getkey
diff --git a/exec/pcboard.src b/exec/pcboard.src
index 8df8eaa0dc..93dc2b6e5b 100644
--- a/exec/pcboard.src
+++ b/exec/pcboard.src
@@ -18,8 +18,8 @@ if_false
 	end_if
 node_action node_main
 async
-print "n\r\nyh(r@MINLEFT@y min. left) @GRP@ (@GN@) @SUB@ (@SN@) "
-print "Command? n"
+print "\x01n\r\n\x01y\x01h(\x01r@MINLEFT@\x01y min. left) @GRP@ (@GN@) @SUB@ (@SN@) "
+print "Command? \x01n"
 getstr 60
 
 compare_str ""
@@ -101,7 +101,7 @@ cmdstr L
 	end_cmd
 
 cmdstr N
-	print "\r\nbhUse c@LASTNEW@b for new file scan date"
+	print "\r\n\x01b\x01hUse \x01c@LASTNEW@\x01b for new file scan date"
 	yes_no ""
 	if_false
 		file_ptrs_cfg
@@ -271,7 +271,7 @@ if_true
 	crlf
 	finduser
 	if_true
-		print "yhVerified: w"
+		print "\x01y\x01hVerified: \x01w"
 		printstr
 		crlf
                 end_if
@@ -279,7 +279,7 @@ if_true
 	end_If
 
 cmdstr USER
-	print "\r\nyhUsername search string (Enter=List Conferences Users): w"
+	print "\r\n\x01y\x01hUsername search string (Enter=List Conferences Users): \x01w"
         getname 25
 	compare_str ""
 	if_true
@@ -288,7 +288,7 @@ cmdstr USER
 		crlf
 		finduser
 		if_true
-			print "yhVerified: w"
+			print "\x01y\x01hVerified: \x01w"
 			printstr
 			crlf
 			end_if
@@ -296,6 +296,6 @@ cmdstr USER
 		end_If
 	end_cmd
 
-print "\r\nrhInvalid Entry!  Please try again, @FIRST@ ...\r\n"
+print "\r\n\x01r\x01hInvalid Entry!  Please try again, @FIRST@ ...\r\n"
 end_cmd
 
diff --git a/exec/wwiv.src b/exec/wwiv.src
index 2d2d4edbcd..32e1a977ed 100644
--- a/exec/wwiv.src
+++ b/exec/wwiv.src
@@ -21,11 +21,11 @@ async
 # Display main Prompt
 compare_ars exempt T
 if_true
-	print "\r\nnT - @TUSED@\n\r"
+	print "\r\n\x01nT - @TUSED@\n\r"
 else
-	print "\r\nnT - @TLEFT@\n\r"
+	print "\r\n\x01nT - @TLEFT@\n\r"
 	end_if
-print "yh(@GN@:@SN@) (@GRP@: @SUBL@) : n"
+print "\x01y\x01h(@GN@:@SN@) (@GRP@: @SUBL@) : \x01n"
 
 # Get key (with / extended commands allowed)
 getkeye
@@ -142,7 +142,7 @@ cmdkey E
 	end_cmd
 
 cmdkey F
-	print "\r\nchFind Text in Messages\r\n"
+	print "\r\n\x01c\x01hFind Text in Messages\r\n"
 	msg_find_text
 	end_cmd
 
@@ -179,7 +179,7 @@ cmdkey $
 	end_cmd
 
 cmdkey N
-	print "\r\nchNew Message Scan\r\n"
+	print "\r\n\x01c\x01hNew Message Scan\r\n"
 	msg_new_scan
 	end_cmd
 
@@ -220,7 +220,7 @@ cmdkey S
 	end_cmd
 
 cmdkey Y
-	print "\r\nchScan for Messages Posted to You\r\n"
+	print "\r\n\x01c\x01hScan for Messages Posted to You\r\n"
 	msg_your_scan
 	end_cmd
 
@@ -229,7 +229,7 @@ cmdkey /S
 	end_cmd
 
 cmdkey U
-	print "\r\nchList Users\r\n"
+	print "\r\n\x01c\x01hList Users\r\n"
 	mnemonics "\r\n~Logons Today, ~Sub-board, or ~All: "
 	getcmd LSA\r
 
@@ -255,7 +255,7 @@ cmdkey .
 	end_cmd
 
 cmdkey Z
-	print "\r\nchContinuous New Message Scan\r\n"
+	print "\r\n\x01c\x01hContinuous New Message Scan\r\n"
 	msg_cont_scan
 	end_cmd
 
@@ -276,18 +276,18 @@ cmdkey &
 	end_cmd
 
 cmdkey #
-	print "\r\nchType the actual number, not the symbol.\r\n"
+	print "\r\n\x01c\x01hType the actual number, not the symbol.\r\n"
 	end_cmd
 
 cmdkey /#
-	print "\r\nchType the actual number, not the symbol.\r\n"
+	print "\r\n\x01c\x01hType the actual number, not the symbol.\r\n"
         end_cmd
 
 # fall through
-print "\r\nchUnrecognized command."
+print "\r\n\x01c\x01hUnrecognized command."
 compare_ars expert
 if_true
-	print " Hit 'i?nch' for a menu."
+	print " Hit '\x01i?\x01n\x01c\x01h' for a menu."
 	end_if
 crlf
 end_cmd
@@ -306,7 +306,7 @@ if_false
 	menu MAININFO
 	end_if
 async
-print "\r\nyhInfo: n"
+print "\r\n\x01y\x01hInfo: \x01n"
 getcmd ?QISVY\r
 logkey
 
@@ -350,7 +350,7 @@ if_false
 	menu MAINCFG
 	end_if
 async
-print "\r\nyhConfig: n"
+print "\r\n\x01y\x01hConfig: \x01n"
 getcmd ?QNPIS\r
 logkey
 
@@ -403,11 +403,11 @@ async
 # Display main Prompt
 compare_ars exempt T
 if_true
-	print "\r\nnT - @TUSED@\n\r"
+	print "\r\n\x01nT - @TUSED@\n\r"
 else
-	print "\r\nnT - @TLEFT@\n\r"
+	print "\r\n\x01nT - @TLEFT@\n\r"
 	end_if
-print "yh[@LN@:@DN@] [@LIB@: @DIRL@] : n"
+print "\x01y\x01h[@LN@:@DN@] [@LIB@: @DIRL@] : \x01n"
 
 # Get key (with / extended commands allowed)
 getkeye
@@ -517,7 +517,7 @@ cmdkey C
 	end_cmd
 
 cmdkey D
-	print "\r\nchDownload File(s)\r\n"
+	print "\r\n\x01c\x01hDownload File(s)\r\n"
 	file_download_batch
 	if_true
 		end_cmd
@@ -529,12 +529,12 @@ cmdkey D
 	end_cmd
 
 cmdkey /D
-	print "\r\nchDownload File(s) from User(s)\r\n"
+	print "\r\n\x01c\x01hDownload File(s) from User(s)\r\n"
 	file_download_user
 	end_cmd
 
 cmdkey E
-	print "\r\nchList Extended File Information\r\n"
+	print "\r\n\x01c\x01hList Extended File Information\r\n"
 	getfilespec
 	if_true
 		file_list_extended
@@ -542,7 +542,7 @@ cmdkey E
 	end_cmd
 
 cmdkey F
-	print "\r\nchFind Text in File Descriptions (no wildcards)\r\n"
+	print "\r\n\x01c\x01hFind Text in File Descriptions (no wildcards)\r\n"
 	file_find_text
 	end_cmd
 
@@ -559,7 +559,7 @@ cmdkey J
 	end_cmd
 
 cmdkey L
-	print "\r\nchList Files\r\n"
+	print "\r\n\x01c\x01hList Files\r\n"
 	getfilespec
 	if_true
 		file_list
@@ -575,7 +575,7 @@ cmdkey W
     end_cmd
 
 cmdkey N
-	print "\r\nchNew File Scan\r\n"
+	print "\r\n\x01c\x01hNew File Scan\r\n"
 	file_new_scan
 	end_cmd
 
@@ -600,7 +600,7 @@ cmdkey /O
         end_cmd
 
 cmdkey R
-	print "\r\nchRemove/Edit File(s)\r\n"
+	print "\r\n\x01c\x01hRemove/Edit File(s)\r\n"
 	getfilespec
 	if_true
 		file_remove
@@ -608,7 +608,7 @@ cmdkey R
 	end_cmd
 
 cmdkey S
-	print "\r\nchSearch for Filename(s)\r\n"
+	print "\r\n\x01c\x01hSearch for Filename(s)\r\n"
 	file_find_name
 	end_cmd
 
@@ -621,17 +621,17 @@ cmdkey G
 	end_cmd
 
 cmdkey U
-	print "\r\nchUpload File\r\n"
+	print "\r\n\x01c\x01hUpload File\r\n"
 	file_upload
 	end_cmd
 
 cmdkey /U
-	print "\r\nchUpload File to User\r\n"
+	print "\r\n\x01c\x01hUpload File to User\r\n"
 	file_upload_user
 	end_cmd
 
 cmdkey V
-	print "\r\nchView File(s)\r\n"
+	print "\r\n\x01c\x01hView File(s)\r\n"
 	getfilespec
 	if_true
 		file_view
@@ -639,7 +639,7 @@ cmdkey V
 	end_cmd
 
 cmdkey Z
-	print "\r\nchUpload File to Sysop\r\n"
+	print "\r\n\x01c\x01hUpload File to Sysop\r\n"
 	file_upload_sysop
 	end_cmd
 
@@ -658,7 +658,7 @@ cmdkey T
 		menu XFERCFG
 		end_if
 	async
-	print "\r\nyhConfig: n"
+	print "\r\n\x01y\x01hConfig: \x01n"
 	getcmd ?QBP\r
 	logkey
 
@@ -680,11 +680,11 @@ cmdkey T
 	end_cmd
 
 cmdkey #
-	print "\r\nchType the actual number, not the symbol.\r\n"
+	print "\r\n\x01c\x01hType the actual number, not the symbol.\r\n"
 	end_cmd
 
 cmdkey /#
-	print "\r\nchType the actual number, not the symbol.\r\n"
+	print "\r\n\x01c\x01hType the actual number, not the symbol.\r\n"
         end_cmd
 
 cmdkey P
@@ -696,10 +696,10 @@ cmdkey Y
 	end_cmd
 
 # fall through
-print "\r\nchUnrecognized command."
+print "\r\n\x01c\x01hUnrecognized command."
 compare_ars expert
 if_true
-	print " Hit 'i?nch' for a menu."
+	print " Hit '\x01i?\x01n\x01c\x01h' for a menu."
 	end_if
 crlf
 end_cmd
@@ -718,7 +718,7 @@ if_false
 	menu XFERINFO
 	end_if
 async
-print "\r\nyhInfo: n"
+print "\r\n\x01y\x01hInfo: \x01n"
 getcmd ?TYDUQ\r
 logkey
 
-- 
GitLab


From c6a16797280c5bd3e2c0e8d27c52e4051516e66b Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 10 Sep 2020 18:09:59 -0700
Subject: [PATCH 653/752] If the text string NewUserPasswordVerify is blank,
 don't require pw verify.

For Alpha (Robbie) for use with an RLogin game door server, apparently.
---
 src/sbbs3/logon.cpp   | 5 ++++-
 src/sbbs3/newuser.cpp | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index 98fdbc9afc..8783f4455f 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -257,8 +257,11 @@ bool sbbs_t::logon()
 					CRLF;
 					bputs(text[VerifyPassword]); 
 				}
-				else
+				else {
+					if(!text[NewUserPasswordVerify][0])
+						break;
 					bputs(text[NewUserPasswordVerify]);
+				}
 				console|=CON_R_ECHOX;
 				getstr(tmp,LEN_PASS*2,K_UPPER);
 				console&=~(CON_R_ECHOX|CON_L_ECHOX);
diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp
index 8e76aff71f..0576109e0a 100644
--- a/src/sbbs3/newuser.cpp
+++ b/src/sbbs3/newuser.cpp
@@ -399,7 +399,7 @@ BOOL sbbs_t::newuser()
 			}
 
 		c=0;
-		while(online) {
+		while(online && text[NewUserPasswordVerify][0]) {
 			bputs(text[NewUserPasswordVerify]);
 			console|=CON_R_ECHOX;
 			str[0]=0;
-- 
GitLab


From 8c9355a6fce03b918593da0958e9fbd04aba7c70 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 00:45:06 -0700
Subject: [PATCH 654/752] Clean up the long-removed-unused code from
 StartTimerTick()

---
 src/sbbs3/ctrl/MainFormUnit.cpp | 41 ---------------------------------
 1 file changed, 41 deletions(-)

diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp
index d90862c3b9..a7aeeb6526 100644
--- a/src/sbbs3/ctrl/MainFormUnit.cpp
+++ b/src/sbbs3/ctrl/MainFormUnit.cpp
@@ -1681,17 +1681,6 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     int		FtpFormPage=PAGE_LOWERRIGHT;
     int		WebFormPage=PAGE_LOWERRIGHT;
     int     ServicesFormPage=PAGE_LOWERRIGHT;
-#if 0   /* not yet working */
-    bool	TelnetFormVisible=true;
-    bool	EventsFormVisible=true;
-    bool	ServicesFormVisible=true;
-    bool 	NodeFormVisible=true;
-    bool	StatsFormVisible=true;
-    bool	ClientFormVisible=true;
-    bool 	MailFormVisible=true;
-    bool	FtpFormVisible=true;
-    bool	WebFormVisible=true;
-#endif
 
     AnsiString	Str;
 
@@ -1741,26 +1730,6 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
         if(Registry->ValueExists("WebFormFloating"))
             WebFormFloating=Registry->ReadBool("WebFormFloating");
     }
-#if 0
-    if(Registry->ValueExists("TelnetFormVisible"))
-        TelnetFormVisible=Registry->ReadBool("TelnetFormVisible");
-    if(Registry->ValueExists("EventsFormVisible"))
-        EventsFormVisible=Registry->ReadBool("EventsFormVisible");
-    if(Registry->ValueExists("ServicesFormVisible"))
-        ServicesFormVisible=Registry->ReadBool("ServicesFormVisible");
-    if(Registry->ValueExists("NodeFormVisible"))
-        NodeFormVisible=Registry->ReadBool("NodeFormVisible");
-    if(Registry->ValueExists("StatsFormVisible"))
-        StatsFormVisible=Registry->ReadBool("StatsFormVisible");
-    if(Registry->ValueExists("ClientFormVisible"))
-        ClientFormVisible=Registry->ReadBool("ClientFormVisible");
-    if(Registry->ValueExists("MailFormVisible"))
-        MailFormVisible=Registry->ReadBool("MailFormVisible");
-    if(Registry->ValueExists("FtpFormVisible"))
-        FtpFormVisible=Registry->ReadBool("FtpFormVisible");
-    if(Registry->ValueExists("WebFormVisible"))
-        WebFormVisible=Registry->ReadBool("WebFormVisible");
-#endif
     if(Registry->ValueExists("TelnetFormPage"))
     	TelnetFormPage=Registry->ReadInteger("TelnetFormPage");
     if(Registry->ValueExists("EventsFormPage"))
@@ -2280,24 +2249,14 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     	WebForm->ManualDock(PageControl(WebFormPage),NULL,alClient);
 
     NodeForm->Show();
-//    ViewNodes->Checked=NodeFormVisible,     ViewNodesExecute(Sender);
     ClientForm->Show();
-//    ViewClients->Checked=ClientFormVisible, ViewClientsExecute(Sender);
     StatsForm->Show();
-//    ViewStats->Checked=StatsFormVisible,    ViewStatsExecute(Sender);
     TelnetForm->Show();
-//    ViewTelnet->Checked=TelnetFormVisible,  ViewTelnetExecute(Sender);
     EventsForm->Show();
-//    ViewEvents->Checked=EventsFormVisible,  ViewEventsExecute(Sender);
     FtpForm->Show();
-//    ViewFtpServer->Checked=FtpFormVisible,  ViewFtpServerExecute(Sender);
     WebForm->Show();
-//    ViewWebServer->Checked=WebFormVisible;
-//    WebForm->Visible=ViewWebServer->Checked;
     MailForm->Show();
-//    ViewMailServer->Checked=MailFormVisible,ViewMailServerExecute(Sender);
     ServicesForm->Show();
-//    ViewServices->Checked=ServicesFormVisible,ViewServicesExecute(Sender);
 
 	UpperLeftPageControl->Visible=true;
 	UpperRightPageControl->Visible=true;
-- 
GitLab


From ae302832626de288dbe5d61349ba2f8ea1368213 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 17:30:09 -0700
Subject: [PATCH 655/752] Add install and symlinks targets

---
 src/sexpots/targets.mk | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/sexpots/targets.mk b/src/sexpots/targets.mk
index b498d6a1cb..7b82013b81 100644
--- a/src/sexpots/targets.mk
+++ b/src/sexpots/targets.mk
@@ -2,4 +2,14 @@ SEXPOTS		=	$(EXEODIR)$(DIRSEP)sexpots$(EXEFILE)
 
 all: xpdev-mt $(MTOBJODIR) $(EXEODIR) $(SEXPOTS)
 
+ifdef SBBSEXEC
+.PHONY: install
+install: all
+	install $(EXEODIR)/* $(SBBSEXEC)
+
+.PHONY: symlinks
+symlinks: all
+	ln -sfr $(EXEODIR)/* $(SBBSEXEC)
+endif
+
 $(SEXPOTS):	$(XPDEV-MT_LIB)
-- 
GitLab


From a40ca56dddef17dd64d464c0de48ad8e7c665f23 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 17:34:47 -0700
Subject: [PATCH 656/752] Resolve GCC warnings.

---
 src/sexpots/sexpots.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/sexpots/sexpots.c b/src/sexpots/sexpots.c
index 568d5b8845..d53fdb510f 100644
--- a/src/sexpots/sexpots.c
+++ b/src/sexpots/sexpots.c
@@ -43,6 +43,7 @@
 #include "dirwrap.h"
 #include "datewrap.h"
 #include "sockwrap.h"
+#include "threadwrap.h"
 #include "ini_file.h"
 
 /* comio lib */
@@ -152,7 +153,7 @@ int usage(const char* fname)
 #endif
 		"\n"
 		,getfname(fname)
-		,NAME);
+		);
 
 	return 0;
 }
@@ -788,8 +789,6 @@ BOOL wait_for_call(COM_HANDLE com_handle)
 {
 	char		str[128];
 	char*		p;
-	BOOL		result=TRUE;
-	DWORD		events=0;
 	time_t		start=time(NULL);
 
 	ZERO_VAR(cid_name);
@@ -835,7 +834,7 @@ BOOL wait_for_call(COM_HANDLE com_handle)
 				if(strncmp(p,"CONNECT ",8)==0) {
 					long rate=atoi(p+8);
 					if(rate)
-						SAFEPRINTF2(termspeed,"%u,%u", rate, rate);
+						SAFEPRINTF2(termspeed,"%ld,%ld", rate, rate);
 				}
 				else if(strncmp(p,"NMBR",4)==0 || strncmp(p,"MESG",4)==0) {
 					p+=4;
@@ -1139,7 +1138,7 @@ BYTE* telnet_interpret(BYTE* inbuf, int inlen, BYTE* outbuf, int *outlen)
 							,telnet_opt_desc(option));
 					/* sub-option terminated */
 					if(option==TELNET_TERM_TYPE && telnet_cmd[3]==TELNET_TERM_SEND) {
-						BYTE buf[32];
+						char buf[32];
 						int len=sprintf(buf,"%c%c%c%c%s%c%c"
 							,TELNET_IAC,TELNET_SB
 							,TELNET_TERM_TYPE,TELNET_TERM_IS
@@ -1150,7 +1149,7 @@ BYTE* telnet_interpret(BYTE* inbuf, int inlen, BYTE* outbuf, int *outlen)
 						sendsocket(sock,buf,len);
 /*						request_telnet_opt(TELNET_WILL, TELNET_TERM_SPEED); */
 					} else if(option==TELNET_TERM_SPEED && telnet_cmd[3]==TELNET_TERM_SEND) {
-						BYTE buf[32];
+						char buf[32];
 						int len=sprintf(buf,"%c%c%c%c%s%c%c"
 							,TELNET_IAC,TELNET_SB
 							,TELNET_TERM_SPEED,TELNET_TERM_IS
@@ -1186,7 +1185,7 @@ BYTE* telnet_interpret(BYTE* inbuf, int inlen, BYTE* outbuf, int *outlen)
 								break;
 							case TELNET_SEND_LOCATION:
 								if(command==TELNET_DO) {
-									BYTE buf[128];
+									char buf[128];
 									int len=safe_snprintf(buf,sizeof(buf),"%c%c%c%s %s%c%c"
 										,TELNET_IAC,TELNET_SB
 										,TELNET_SEND_LOCATION
-- 
GitLab


From e1534a746cf8e4cbe0417560ada9e789833e0e39 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 17:53:10 -0700
Subject: [PATCH 657/752] Update Id keyword. Not sure why.

---
 src/sexpots/sexpots.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sexpots/sexpots.c b/src/sexpots/sexpots.c
index d53fdb510f..3866f571ad 100644
--- a/src/sexpots/sexpots.c
+++ b/src/sexpots/sexpots.c
@@ -2,7 +2,7 @@
 
 /* Synchronet External Plain Old Telephone System (POTS) support */
 
-/* $Id: sexpots.c,v 1.32 2019/05/05 22:48:33 rswindell Exp $ */
+/* $Id: sexpots.c,v 1.33 2020/09/11 22:48:33 rswindell Exp $ */
 
 /****************************************************************************
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
-- 
GitLab


From 8d864dbed580f7a30cd624c89b17bb3df903991e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 18:21:20 -0700
Subject: [PATCH 658/752] OpenBSD is not expressly supported any more (sorry).

---
 text/sbbs.msg | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/text/sbbs.msg b/text/sbbs.msg
index 7dd0d8e3ca..711e55aefa 100644
--- a/text/sbbs.msg
+++ b/text/sbbs.msg
@@ -10,7 +10,7 @@
 w              Synchronet BBS for @PLATFORM@ - Version @FULL_VER@
 w              @COPYRIGHT@ - http://www.synchro.net
 gh
-32-bit Multi-threaded Bulletin Board System Software for the Internet Age
+Native Multi-threaded Bulletin Board System Software for the Internet Age
 ng
 Integrated TCP/IP Servers: hong Telnet/RLogin/SSH
                            hong SMTP/POP3 (inbound and outbound e-mail)
@@ -19,7 +19,7 @@ Integrated TCP/IP Servers: hong Telnet/RLogin/SSH
                            hong Web (HTTP with server-side JavaScript and CGI)
                            hong Finger, Gopher, and IRC
 h
-Native versions for Win32 and Unix (e.g. Linux, FreeBSD, and OpenBSD) platforms
+Native versions for Win32 and Unix-like (e.g. Linux and FreeBSD) platforms
 n@CLS@-----------------------------------------------------------------------------
 hRemembering these keys will help you enjoy your cSynchronetw Telnet experience:
 n-----------------------------------------------------------------------------
-- 
GitLab


From e1a79a56a29f8dd59af32fd51c88cfcc96242a25 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 19:23:39 -0700
Subject: [PATCH 659/752] Improve startup w/Config Wizard reliability

I noticed on one particular system that Canceling or Completing
the configuration wizard on a fresh install, sbbsctrl.exe would
just shut down (no error dialog or anything, likely a crash of
some kind). Instrumenting StartupTimerTick() didn't reveal anything
useful (it ran to completion).

By changing the method of launching the Configuration Wizard,
I was able to eliminate this observed problem. Now, the StartupTimer
runs twice on a fresh install (just once for a normal startup),
and the second run of the StartupTimerTick starts the configuration
wizard.

I also reverted to the previous behavior of dynamically creating
and destroying the wizard for each use. There's just too much state
information to restore if the config wizard is run a second time.

Also, removed a bunch of old Registry settings readings (v3.10/11
upgrade support) and commented out code.
---
 src/sbbs3/ctrl/ConfigWizardUnit.cpp |   3 +-
 src/sbbs3/ctrl/MainFormUnit.cpp     | 356 +++++-----------------------
 src/sbbs3/ctrl/MainFormUnit.h       |   1 +
 3 files changed, 66 insertions(+), 294 deletions(-)

diff --git a/src/sbbs3/ctrl/ConfigWizardUnit.cpp b/src/sbbs3/ctrl/ConfigWizardUnit.cpp
index c8ba1b5d6b..32667eaecd 100644
--- a/src/sbbs3/ctrl/ConfigWizardUnit.cpp
+++ b/src/sbbs3/ctrl/ConfigWizardUnit.cpp
@@ -262,6 +262,7 @@ void __fastcall TConfigWizard::FormShow(TObject *Sender)
         DateUsRadioButton->Checked=true;
 
     WizNotebook->PageIndex=0;
+	ProgressBar->Position=0;
     ProgressBar->Max=WizNotebook->Pages->Count-1;
     IllegalCharsLabel->Caption="Illegal characters: '"
         ILLEGAL_QWKID_CHARS "'";
@@ -323,14 +324,12 @@ void __fastcall TConfigWizard::NextButtonClick(TObject *Sender)
             scfg.sys_misc|=SM_SYSVDELM;
             scfg.sys_misc&=~SM_USRVDELM;
         }
-
         scfg.new_install=FALSE;
         if(!save_cfg(&scfg,0)) {
         	Application->MessageBox("Error saving configuration"
             	,"ERROR",MB_OK|MB_ICONEXCLAMATION);
         } else
         	refresh_cfg(&scfg);
-
         Close();
         return;
     }
diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp
index a7aeeb6526..8ee17f4972 100644
--- a/src/sbbs3/ctrl/MainFormUnit.cpp
+++ b/src/sbbs3/ctrl/MainFormUnit.cpp
@@ -1682,9 +1682,15 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     int		WebFormPage=PAGE_LOWERRIGHT;
     int     ServicesFormPage=PAGE_LOWERRIGHT;
 
-    AnsiString	Str;
+	StartupTimer->Enabled = false;
+	if(Initialized) { // second time (fresh install)
+		delete StartupTimer;
+        BBSConfigWizardMenuItemClick(Sender);
+		DisplayMainPanels(Sender);
+		return;
+	}
 
-    delete StartupTimer;
+    AnsiString	Str;
 
     // Read Registry keys
 	TRegistry* Registry=new TRegistry;
@@ -1916,263 +1922,27 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     else
     	FtpLogFile=true;
 
-    FILE* fp;
-    if((!Registry->ValueExists("SysAutoStart")
-        || (Registry->ValueExists("Imported") && Registry->ReadBool("Imported")))
-        && ini_file[0]) {
-        if((fp=fopen(ini_file,"r"))==NULL) {
-            char err[MAX_PATH*2];
-            sprintf(err,"Error %d opening initialization file: %s",errno,ini_file);
-            Application->MessageBox(err,"ERROR",MB_OK|MB_ICONEXCLAMATION);
-            Application->Terminate();
-            return;
-        }
-        sbbs_read_ini(fp, MainForm->ini_file
-            ,&global
-            ,&SysAutoStart   		,&bbs_startup
-            ,&FtpAutoStart 			,&ftp_startup
-            ,&WebAutoStart 			,&web_startup
-            ,&MailAutoStart 	    ,&mail_startup
-            ,&ServicesAutoStart     ,&services_startup
-            );
-       	StatusBar->Panels->Items[STATUSBAR_LAST_PANEL]->Text="Read " + AnsiString(ini_file);
-        fclose(fp);
-
-    } else {    /* Legacy (v3.10-3.11) */
-
-        if(Registry->ValueExists("SysAutoStart"))
-            SysAutoStart=Registry->ReadInteger("SysAutoStart");
-        else
-            SysAutoStart=true;
-
-        if(Registry->ValueExists("MailAutoStart"))
-            MailAutoStart=Registry->ReadInteger("MailAutoStart");
-        else
-            MailAutoStart=true;
-
-        if(Registry->ValueExists("FtpAutoStart"))
-            FtpAutoStart=Registry->ReadInteger("FtpAutoStart");
-        else
-            FtpAutoStart=true;
-
-        if(Registry->ValueExists("WebAutoStart"))
-            WebAutoStart=Registry->ReadInteger("WebAutoStart");
-        else
-            WebAutoStart=true;
-
-        if(Registry->ValueExists("ServicesAutoStart"))
-            ServicesAutoStart=Registry->ReadInteger("ServicesAutoStart");
-        else
-            ServicesAutoStart=true;
-
-        if(Registry->ValueExists("Hostname"))
-            SAFECOPY(global.host_name,Registry->ReadString("Hostname").c_str());
-		if(Registry->ValueExists("CtrlDirectory"))
-            SAFECOPY(global.ctrl_dir,Registry->ReadString("CtrlDirectory").c_str());
-        if(Registry->ValueExists("TempDirectory"))
-            SAFECOPY(global.temp_dir,Registry->ReadString("TempDirectory").c_str());
-
-        if(Registry->ValueExists("SemFileCheckFrequency"))
-            global.sem_chk_freq=Registry->ReadInteger("SemFileCheckFrequency");
-
-        /* JavaScript Operating Parameters */
-        if(Registry->ValueExists("JS_MaxBytes"))
-            global.js.max_bytes=Registry->ReadInteger("JS_MaxBytes");
-        if(global.js.max_bytes==0)
-            global.js.max_bytes=JAVASCRIPT_MAX_BYTES;
-        if(Registry->ValueExists("JS_ContextStack"))
-            global.js.cx_stack=Registry->ReadInteger("JS_ContextStack");
-        if(global.js.cx_stack==0)
-            global.js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
-        if(Registry->ValueExists("JS_GcInterval"))
-            global.js.gc_interval=Registry->ReadInteger("JS_GcInterval");
-        if(Registry->ValueExists("JS_YieldInterval"))
-            global.js.yield_interval=Registry->ReadInteger("JS_YieldInterval");
-
-/*
-        if(Registry->ValueExists("TelnetInterface"))
-            bbs_startup.telnet_interface=Registry->ReadInteger("TelnetInterface");
-        if(Registry->ValueExists("RLoginInterface"))
-            bbs_startup.rlogin_interface=Registry->ReadInteger("RLoginInterface");
-*/
-
-        if(Registry->ValueExists("TelnetPort"))
-            bbs_startup.telnet_port=Registry->ReadInteger("TelnetPort");
-        if(Registry->ValueExists("RLoginPort"))
-            bbs_startup.rlogin_port=Registry->ReadInteger("RLoginPort");
-
-        if(Registry->ValueExists("FirstNode"))
-            bbs_startup.first_node=Registry->ReadInteger("FirstNode");
-
-        if(Registry->ValueExists("LastNode"))
-            bbs_startup.last_node=Registry->ReadInteger("LastNode");
-
-        if(Registry->ValueExists("OutbufHighwaterMark"))
-            bbs_startup.outbuf_highwater_mark=Registry->ReadInteger("OutbufHighwaterMark");
-        else
-            bbs_startup.outbuf_highwater_mark=1024;
-        if(Registry->ValueExists("OutbufDrainTimeout"))
-            bbs_startup.outbuf_drain_timeout=Registry->ReadInteger("OutbufDrainTimeout");
-        else
-            bbs_startup.outbuf_drain_timeout=10;
-
-        if(Registry->ValueExists("AnswerSound"))
-            SAFECOPY(bbs_startup.answer_sound
-                ,Registry->ReadString("AnswerSound").c_str());
-
-        if(Registry->ValueExists("HangupSound"))
-            SAFECOPY(bbs_startup.hangup_sound
-                ,Registry->ReadString("HangupSound").c_str());
-
-        if(Registry->ValueExists("StartupOptions"))
-            bbs_startup.options=Registry->ReadInteger("StartupOptions");
-
-        if(Registry->ValueExists("MailMaxClients"))
-            mail_startup.max_clients=Registry->ReadInteger("MailMaxClients");
-
-        if(Registry->ValueExists("MailMaxInactivity"))
-            mail_startup.max_inactivity=Registry->ReadInteger("MailMaxInactivity");
-
-/*
-        if(Registry->ValueExists("MailInterface"))
-            mail_startup.interface_addr=Registry->ReadInteger("MailInterface");
-*/
-
-        if(Registry->ValueExists("MailMaxDeliveryAttempts"))
-            mail_startup.max_delivery_attempts
-                =Registry->ReadInteger("MailMaxDeliveryAttempts");
-
-        if(Registry->ValueExists("MailRescanFrequency"))
-            mail_startup.rescan_frequency
-                =Registry->ReadInteger("MailRescanFrequency");
-
-        if(Registry->ValueExists("MailLinesPerYield"))
-            mail_startup.lines_per_yield
-                =Registry->ReadInteger("MailLinesPerYield");
-
-        if(Registry->ValueExists("MailMaxRecipients"))
-            mail_startup.max_recipients
-                =Registry->ReadInteger("MailMaxRecipients");
-
-        if(Registry->ValueExists("MailMaxMsgSize"))
-            mail_startup.max_msg_size
-                =Registry->ReadInteger("MailMaxMsgSize");
-
-        if(Registry->ValueExists("MailSMTPPort"))
-            mail_startup.smtp_port=Registry->ReadInteger("MailSMTPPort");
-
-        if(Registry->ValueExists("MailPOP3Port"))
-            mail_startup.pop3_port=Registry->ReadInteger("MailPOP3Port");
-
-        if(Registry->ValueExists("MailRelayServer"))
-            SAFECOPY(mail_startup.relay_server
-                ,Registry->ReadString("MailRelayServer").c_str());
-
-        if(Registry->ValueExists("MailRelayPort"))
-            mail_startup.relay_port=Registry->ReadInteger("MailRelayPort");
-
-        if(Registry->ValueExists("MailDefaultUser"))
-            SAFECOPY(mail_startup.default_user
-                ,Registry->ReadString("MailDefaultUser").c_str());
-
-        if(Registry->ValueExists("MailDNSBlacklistSubject"))
-            SAFECOPY(mail_startup.dnsbl_tag
-                ,Registry->ReadString("MailDNSBlacklistSubject").c_str());
-        else
-            SAFECOPY(mail_startup.dnsbl_tag,"SPAM");
-
-        if(Registry->ValueExists("MailDNSBlacklistHeader"))
-            SAFECOPY(mail_startup.dnsbl_hdr
-                ,Registry->ReadString("MailDNSBlacklistHeader").c_str());
-        else
-            SAFECOPY(mail_startup.dnsbl_hdr,"X-DNSBL");
-
-        if(Registry->ValueExists("MailDNSServer"))
-            SAFECOPY(mail_startup.dns_server
-                ,Registry->ReadString("MailDNSServer").c_str());
-
-        if(Registry->ValueExists("MailInboundSound"))
-            SAFECOPY(mail_startup.inbound_sound
-                ,Registry->ReadString("MailInboundSound").c_str());
-
-        if(Registry->ValueExists("MailOutboundSound"))
-            SAFECOPY(mail_startup.outbound_sound
-                ,Registry->ReadString("MailOutboundSound").c_str());
-
-        if(Registry->ValueExists("MailPOP3Sound"))
-            SAFECOPY(mail_startup.pop3_sound
-                ,Registry->ReadString("MailPOP3Sound").c_str());
-
-        if(Registry->ValueExists("MailOptions"))
-            mail_startup.options=Registry->ReadInteger("MailOptions");
-
-        if(Registry->ValueExists("FtpMaxClients"))
-            ftp_startup.max_clients=Registry->ReadInteger("FtpMaxClients");
-
-        if(Registry->ValueExists("FtpMaxInactivity"))
-            ftp_startup.max_inactivity=Registry->ReadInteger("FtpMaxInactivity");
-
-        if(Registry->ValueExists("FtpQwkTimeout"))
-            ftp_startup.qwk_timeout=Registry->ReadInteger("FtpQwkTimeout");
-
-/*
-        if(Registry->ValueExists("FtpInterface"))
-            ftp_startup.interface_addr=Registry->ReadInteger("FtpInterface");
-*/
-
-        if(Registry->ValueExists("FtpPort"))
-            ftp_startup.port=Registry->ReadInteger("FtpPort");
-
-        if(Registry->ValueExists("FtpAnswerSound"))
-            SAFECOPY(ftp_startup.answer_sound
-                ,Registry->ReadString("FtpAnswerSound").c_str());
-
-        if(Registry->ValueExists("FtpHangupSound"))
-            SAFECOPY(ftp_startup.hangup_sound
-                ,Registry->ReadString("FtpHangupSound").c_str());
-
-        if(Registry->ValueExists("FtpHackAttemptSound"))
-            SAFECOPY(ftp_startup.hack_sound
-                ,Registry->ReadString("FtpHackAttemptSound").c_str());
-
-        if(Registry->ValueExists("FtpIndexFileName"))
-            SAFECOPY(ftp_startup.index_file_name
-                ,Registry->ReadString("FtpIndexFileName").c_str());
-
-        if(Registry->ValueExists("FtpHtmlIndexFile"))
-            SAFECOPY(ftp_startup.html_index_file
-                ,Registry->ReadString("FtpHtmlIndexFile").c_str());
-
-        if(Registry->ValueExists("FtpHtmlIndexScript"))
-            SAFECOPY(ftp_startup.html_index_script
-                ,Registry->ReadString("FtpHtmlIndexScript").c_str());
-
-        if(Registry->ValueExists("FtpOptions"))
-            ftp_startup.options=Registry->ReadInteger("FtpOptions");
-
-/*
-        if(Registry->ValueExists("ServicesInterface"))
-            services_startup.interface_addr
-                =Registry->ReadInteger("ServicesInterface");
-*/
-
-        if(Registry->ValueExists("ServicesAnswerSound"))
-            SAFECOPY(services_startup.answer_sound
-                ,Registry->ReadString("ServicesAnswerSound").c_str());
-
-        if(Registry->ValueExists("ServicesHangupSound"))
-            SAFECOPY(services_startup.hangup_sound
-                ,Registry->ReadString("ServicesHangupSound").c_str());
-
-        if(Registry->ValueExists("ServicesOptions"))
-            services_startup.options=Registry->ReadInteger("ServicesOptions");
+	Registry->CloseKey();
+    delete Registry;
 
-		if(SaveIniSettings(Sender))
-            Registry->WriteBool("Imported",true);   /* Use the .ini file for these settings from now on */
+    FILE* fp;
+    if((fp=fopen(ini_file,"r"))==NULL) {
+        char err[MAX_PATH*2];
+        sprintf(err,"Error %d opening initialization file: %s",errno,ini_file);
+        Application->MessageBox(err,"ERROR",MB_OK|MB_ICONEXCLAMATION);
+        Application->Terminate();
+        return;
     }
-
-    Registry->CloseKey();
-    delete Registry;
+    sbbs_read_ini(fp, MainForm->ini_file
+        ,&global
+        ,&SysAutoStart   		,&bbs_startup
+        ,&FtpAutoStart 			,&ftp_startup
+        ,&WebAutoStart 			,&web_startup
+        ,&MailAutoStart 	    ,&mail_startup
+        ,&ServicesAutoStart     ,&services_startup
+        );
+    StatusBar->Panels->Items[STATUSBAR_LAST_PANEL]->Text="Read " + AnsiString(ini_file);
+    fclose(fp);
 
     AnsiString CtrlDirectory = AnsiString(global.ctrl_dir);
     if(!FileExists(CtrlDirectory+"MAIN.CNF")) {
@@ -2203,32 +1973,6 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     }
    	StatusBar->Panels->Items[STATUSBAR_LAST_PANEL]->Text="Configuration loaded";
 
-	recycle_semfiles=semfile_list_init(cfg.ctrl_dir,"recycle","ctrl");
-    semfile_list_add(&recycle_semfiles,ini_file);
-   	semfile_list_check(&initialized,recycle_semfiles);
-
-	shutdown_semfiles=semfile_list_init(cfg.ctrl_dir,"shutdown","ctrl");
-	semfile_list_check(&initialized,shutdown_semfiles);
-
-    if(cfg.new_install) {
-    	Application->BringToFront();
-        for(int i=0;i<10;i++) {
-	        Application->ProcessMessages();
-	    	Sleep(300);	// Let 'em see the logo for a bit
-        }
-        BBSConfigWizardMenuItemClick(Sender);
-    }
-
-    if(bbs_startup.options&BBS_OPT_MUTE)
-    	SoundToggle->Checked=false;
-    else
-    	SoundToggle->Checked=true;
-
-    if(sysop_available(&cfg))
-    	ChatToggle->Checked=true;
-    else
-    	ChatToggle->Checked=false;
-
    	if(!NodeFormFloating)
     	NodeForm->ManualDock(PageControl(NodeFormPage),NULL,alClient);
 	if(!ClientFormFloating)
@@ -2248,6 +1992,35 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
 	if(!WebFormFloating)
     	WebForm->ManualDock(PageControl(WebFormPage),NULL,alClient);
 
+	recycle_semfiles=semfile_list_init(cfg.ctrl_dir,"recycle","ctrl");
+	semfile_list_add(&recycle_semfiles,ini_file);
+	semfile_list_check(&initialized,recycle_semfiles);
+
+	shutdown_semfiles=semfile_list_init(cfg.ctrl_dir,"shutdown","ctrl");
+	semfile_list_check(&initialized,shutdown_semfiles);
+
+    if(cfg.new_install) {
+		Application->BringToFront();
+		StartupTimer->Interval = 2500;	// Let 'em see the logo for a bit
+		StartupTimer->Enabled = true;
+	} else {
+		DisplayMainPanels(Sender);
+	}
+    Initialized=true;
+}
+
+void __fastcall TMainForm::DisplayMainPanels(TObject* Sender)
+{
+	if(bbs_startup.options&BBS_OPT_MUTE)
+		SoundToggle->Checked=false;
+	else
+		SoundToggle->Checked=true;
+
+	if(sysop_available(&cfg))
+		ChatToggle->Checked=true;
+	else
+		ChatToggle->Checked=false;
+
     NodeForm->Show();
     ClientForm->Show();
     StatsForm->Show();
@@ -2263,8 +2036,8 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
 	LowerLeftPageControl->Visible=true;
 	LowerRightPageControl->Visible=true;
 	HorizontalSplitter->Visible=true;
-    BottomPanel->Visible=true;
-   	TopPanel->Visible=true;
+	BottomPanel->Visible=true;
+	TopPanel->Visible=true;
 
     // Work-around for CB5 PageControl anomaly
     int i;
@@ -2317,12 +2090,11 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     ServiceStatusTimer->Enabled=true;
 
 	SetControls();
-	
-    if(!Application->Active)	/* Starting up minimized? */
-    	FormMinimize(Sender);   /* Put icon in systray */
 
-    Initialized=true;
+	if(!Application->Active)	/* Starting up minimized? */
+		FormMinimize(Sender);   /* Put icon in systray */
 }
+
 //---------------------------------------------------------------------------
 void __fastcall TMainForm::SaveRegistrySettings(TObject* Sender)
 {
@@ -3320,17 +3092,17 @@ void __fastcall TMainForm::RestoreTrayMenuItemClick(TObject *Sender)
 //---------------------------------------------------------------------------
 void __fastcall TMainForm::BBSConfigWizardMenuItemClick(TObject *Sender)
 {
-    static TConfigWizard* ConfigWizard;
+    TConfigWizard* ConfigWizard;
     static inside;
     if(inside) return;
     inside=true;
 
-	if(ConfigWizard == NULL)
-	    Application->CreateForm(__classid(TConfigWizard), &ConfigWizard);
+    Application->CreateForm(__classid(TConfigWizard), &ConfigWizard);
 	if(ConfigWizard->ShowModal()==mrOk) {
         SaveSettings(Sender);
 //        ReloadConfigExecute(Sender);  /* unnecessary since refresh_cfg() is already called */
     }
+	delete ConfigWizard;
 
     inside=false;
 }
diff --git a/src/sbbs3/ctrl/MainFormUnit.h b/src/sbbs3/ctrl/MainFormUnit.h
index 564ec7bec6..0c1463ede7 100644
--- a/src/sbbs3/ctrl/MainFormUnit.h
+++ b/src/sbbs3/ctrl/MainFormUnit.h
@@ -492,6 +492,7 @@ public:		// User declarations
 	TPageControl* __fastcall PageControl(int num);
 	int __fastcall  PageNum(TPageControl* obj);
     void __fastcall FormMinimize(TObject *Sender);
+	void __fastcall DisplayMainPanels(TObject *Sender);
     TColor __fastcall ReadColor(TRegistry*, AnsiString);
     void __fastcall WriteColor(TRegistry*, AnsiString, TColor);
     void __fastcall ReadFont(AnsiString, TFont*);
-- 
GitLab


From 3eb83ec5e7b10343aa5b3b04e93cf83b81343b2a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 19:30:03 -0700
Subject: [PATCH 660/752] Don't allow invalid netmail-forwarding addresses

Don't prompt a new user to forward email to their netmail address if they
provided an invalid netmail address (not supported by the system).

If a sysop has an invalid netmail address setup for forwarding, don't try to
forward email (or new user feedback) to that address.

Re-ordered the new user terminal questions a little bit (ask for the backspace
key first, to get earlier manual PETSCII detection). I'm not sure why I was
enabling AUTOTERM along with PETSCII before. Removed that as PETSCII cannot
be auto-detected.
---
 src/sbbs3/email.cpp    | 12 +++++++----
 src/sbbs3/netmail.cpp  | 20 ++++++++++++++++++
 src/sbbs3/newuser.cpp  | 46 +++++++++++++++++++++---------------------
 src/sbbs3/sbbs.h       |  3 +++
 src/sbbs3/useredit.cpp |  8 +++-----
 5 files changed, 57 insertions(+), 32 deletions(-)

diff --git a/src/sbbs3/email.cpp b/src/sbbs3/email.cpp
index 09de214750..164468676d 100644
--- a/src/sbbs3/email.cpp
+++ b/src/sbbs3/email.cpp
@@ -91,11 +91,15 @@ bool sbbs_t::email(int usernumber, const char *top, const char *subj, long mode,
 		bputs(text[UnknownUser]);
 		return(false); 
 	}
-	if((l&NETMAIL) && (cfg.sys_misc&SM_FWDTONET) && !(mode & WM_NOFWD)) {
+	if((l&NETMAIL) && (cfg.sys_misc&SM_FWDTONET) && !(mode & WM_NOFWD) && !(useron.rest&FLAG('M'))) {
 		getuserrec(&cfg,usernumber,U_NETMAIL,LEN_NETMAIL,str);
-		bprintf(text[UserNetMail],str);
-		if((mode & WM_FORCEFWD) || text[ForwardMailQ][0]==0 || yesno(text[ForwardMailQ])) /* Forward to netmail address */
-			return(netmail(str, subj, mode, resmb, remsg));
+		if(is_supported_netmail_addr(&cfg, str)) {
+			bprintf(text[UserNetMail],str);
+			if((mode & WM_FORCEFWD) || yesno(text[ForwardMailQ])) /* Forward to netmail address */
+				return(netmail(str, subj, mode, resmb, remsg));
+		} else {
+			bprintf(text[InvalidNetMailAddr], str);
+		}
 	}
 	if(sys_status&SS_ABORT) {
 		bputs(text[Aborted]);
diff --git a/src/sbbs3/netmail.cpp b/src/sbbs3/netmail.cpp
index 80e9476171..bffbe9aad8 100644
--- a/src/sbbs3/netmail.cpp
+++ b/src/sbbs3/netmail.cpp
@@ -1428,3 +1428,23 @@ bool sbbs_t::qnetmail(const char *into, const char *subj, long mode, smb_t* resm
 	logline("EN",str);
 	return(true);
 }
+
+extern "C" BOOL is_supported_netmail_addr(scfg_t* cfg, const char* addr)
+{
+	switch (smb_netaddr_type(addr)) {
+		case NET_FIDO:
+			return INT_TO_BOOL(cfg->total_faddrs && (cfg->netmail_misc&NMAIL_ALLOW));
+		case NET_INTERNET:
+			return INT_TO_BOOL(cfg->inetmail_misc&NMAIL_ALLOW);
+		case NET_QWK:
+		{
+			char fulladdr[256] = "";
+			const char* p = strchr(addr, '@');
+			if(p == NULL)
+				return FALSE;
+			qwk_route(cfg, p + 1, fulladdr, sizeof(fulladdr)-1);
+			return fulladdr[0] != 0;
+		}
+	}
+	return FALSE;
+}
diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp
index 0576109e0a..da773b4116 100644
--- a/src/sbbs3/newuser.cpp
+++ b/src/sbbs3/newuser.cpp
@@ -152,7 +152,26 @@ BOOL sbbs_t::newuser()
 		} else
 			useron.misc&=~AUTOTERM;
 
-		if(!(useron.misc&AUTOTERM)) {
+		while(text[HitYourBackspaceKey][0] && !(useron.misc&(PETSCII|SWAP_DELETE)) && online) {
+			bputs(text[HitYourBackspaceKey]);
+			uchar key = getkey(K_NONE);
+			bprintf(text[CharacterReceivedFmt], key, key);
+			if(key == '\b')
+				break;
+			if(key == DEL) {
+				if(text[SwapDeleteKeyQ][0] == 0 || yesno(text[SwapDeleteKeyQ]))
+					useron.misc |= SWAP_DELETE;
+			}
+			else if(key == PETSCII_DELETE)
+				useron.misc |= (PETSCII|COLOR);
+			else {
+				bprintf(text[InvalidBackspaceKeyFmt], key, key);
+				if(text[ContinueQ][0] && !yesno(text[ContinueQ]))
+					return FALSE;
+			}
+		}
+
+		if(!(useron.misc&(AUTOTERM|PETSCII))) {
 			if(text[AnsiTerminalQ][0] && yesno(text[AnsiTerminalQ]))
 				useron.misc|=ANSI; 
 			else
@@ -173,25 +192,6 @@ BOOL sbbs_t::newuser()
 		else
 			useron.rows = TERM_ROWS_DEFAULT;
 
-		while(text[HitYourBackspaceKey][0] && !(useron.misc&(PETSCII|SWAP_DELETE)) && online) {
-			bputs(text[HitYourBackspaceKey]);
-			uchar key = getkey(K_NONE);
-			bprintf(text[CharacterReceivedFmt], key, key);
-			if(key == '\b')
-				break;
-			if(key == DEL) {
-				if(text[SwapDeleteKeyQ][0] == 0 || yesno(text[SwapDeleteKeyQ]))
-					useron.misc |= SWAP_DELETE;
-			}
-			else if(key == PETSCII_DELETE)
-				useron.misc |= (AUTOTERM|PETSCII|COLOR);
-			else {
-				bprintf(text[InvalidBackspaceKeyFmt], key, key);
-				if(text[ContinueQ][0] && !yesno(text[ContinueQ]))
-					return FALSE;
-			}
-		}
-
 		if(useron.misc&PETSCII) {
 			autoterm |= PETSCII;
 			outcom(PETSCII_UPPERLOWER);
@@ -328,10 +328,10 @@ BOOL sbbs_t::newuser()
 				&& !trashcan(useron.netmail,"email"))
 				break;
 		}
-		if(useron.netmail[0] && cfg.sys_misc&SM_FWDTONET && text[ForwardMailQ][0] && yesno(text[ForwardMailQ]))
+		useron.misc&=~NETMAIL;
+		if((cfg.sys_misc&SM_FWDTONET) && is_supported_netmail_addr(&cfg, useron.netmail) && yesno(text[ForwardMailQ]))
 			useron.misc|=NETMAIL;
-		else 
-			useron.misc&=~NETMAIL;
+
 		if(text[UserInfoCorrectQ][0]==0 || yesno(text[UserInfoCorrectQ]))
 			break; 
 	}
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index dd2b758a10..b4bf668eea 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -1294,6 +1294,9 @@ extern "C" {
 	/* qwk.cpp */
 	DLLEXPORT int		qwk_route(scfg_t*, const char *inaddr, char *fulladdr, size_t maxlen);
 
+	/* netmail.cpp */
+	DLLEXPORT BOOL		is_supported_netmail_addr(scfg_t*, const char* addr);
+
 	/* con_out.cpp */
 	unsigned char		cp437_to_petscii(unsigned char);
 
diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp
index 787a2953c0..74857c0003 100644
--- a/src/sbbs3/useredit.cpp
+++ b/src/sbbs3/useredit.cpp
@@ -1082,11 +1082,9 @@ void sbbs_t::maindflts(user_t* user)
 				if(sys_status&SS_ABORT)
 					break;
 				putuserrec(&cfg,user->number,U_NETMAIL,LEN_NETMAIL,user->netmail); 
-				if(user->netmail[0] == 0 || noyes(text[ForwardMailQ]))
-					user->misc&=~NETMAIL;
-				else {
-					user->misc|=NETMAIL;
-				}
+				user->misc &= ~NETMAIL;
+				if(is_supported_netmail_addr(&cfg, user->netmail) && !noyes(text[ForwardMailQ]))
+					user->misc |= NETMAIL;
 				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
 				break;
 			case 'C':
-- 
GitLab


From 70eace090ed0b7c43c30633c6cf9cab3c4496b67 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 11 Sep 2020 22:03:48 -0700
Subject: [PATCH 661/752] When not using -auto, prompt for which
 xtrn/*/install-xtrn.ini file to install

---
 exec/install-xtrn.js | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index 6080c336d2..724fb68adb 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -436,16 +436,43 @@ for (var i = 0; i < argc; i++) {
 		ini_list.push(argv[i]);
 }
 
+function find_startup_dir(dir)
+{
+	for (var i=0; i < xtrn_area.prog.length; i++) {
+		if (xtrn_area.prog[i].startup_dir.toLowerCase() == dir.toLowerCase())
+			return i;
+	}
+	return -1;
+}
+
 var xtrn_dirs = fullpath(system.ctrl_dir + "../xtrn/*");
 if(!ini_list.length) {
 	var dir_list = directory(xtrn_dirs);
 	for(var d in dir_list) {
+		if(!options.overwrite && find_startup_dir(dir_list[d]))
+			continue;
 		var fname = file_getcase(dir_list[d] + ini_fname);
 		if(fname)
 			ini_list.push(fname);
 	}
 }
 
+if(!options.auto && ini_list.length > 1) {
+	for(var i = 0; i < ini_list.length; i++) {
+		printf("%3d: %s\r\n", i+1, ini_list[i].substr(0, ini_list[i].length - ini_fname.length));
+	}
+	var which;
+	while(!which || which < 1 || which > ini_list.length) {
+		var str = prompt("Which or [Q]uit");
+		if(aborted())
+			exit(0);
+		if(str && str.toUpperCase() == 'Q')
+			exit(0);
+		which = parseInt(str, 10);
+	}
+	ini_list = [ini_list[which - 1]];
+}
+
 if(!ini_list.length) {
 	if(options.auto) {
 		alert("No install files (" + ini_fname + ") found in " + xtrn_dirs);
@@ -454,6 +481,8 @@ if(!ini_list.length) {
 	var ini_path;
 	while (!ini_path || !file_exists(ini_path)) {
 		ini_path = prompt("Location of " + ini_fname);
+		if(aborted())
+			exit(0);
 		if (file_isdir(ini_path))
 			ini_path = backslash(ini_path) + ini_fname;
 	}
-- 
GitLab


From 3d34959ec17c42c81b63889fe871bfe86f0331ac Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 12:28:13 -0700
Subject: [PATCH 662/752] Let the installer allow easy data-share with
 bbs.electronicchicken.com

Create a default server.ini pointing to localhost:10088 (same behavior as
if the file is absent) and allow the installer to modify it to point to
the Executive Chiggun's B.B.S. if the sysop opts-in.
---
 xtrn/lemons/install-xtrn.ini | 8 +++++++-
 xtrn/lemons/server.ini       | 2 ++
 2 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 xtrn/lemons/server.ini

diff --git a/xtrn/lemons/install-xtrn.ini b/xtrn/lemons/install-xtrn.ini
index 403500a10a..b956a548b5 100644
--- a/xtrn/lemons/install-xtrn.ini
+++ b/xtrn/lemons/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: <MegaloYeti> someone needs to make a new lemmings game <echicken> claimed
 By:   echicken -at- bbs.electronicchicken.com
 Cats: Games
 Subs: Platfomer, Clone, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 08:13:20 rswindell Exp $
+Inst: 2020/09/11
 
 [prog:LEMONS]
 cmd  = ?lemons.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with electronic chicken bbs
+keys = host
+values = 'bbs.electronicchicken.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:lemons]
diff --git a/xtrn/lemons/server.ini b/xtrn/lemons/server.ini
new file mode 100644
index 0000000000..aa1e15cde4
--- /dev/null
+++ b/xtrn/lemons/server.ini
@@ -0,0 +1,2 @@
+host=localhost
+port=10088
-- 
GitLab


From 08f3b2971e63ccc08a9990b781b3e860efc96819 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 12:31:29 -0700
Subject: [PATCH 663/752] Fix bug in previous commit to this file
 find_startup_dir() returns an index, not a bool.

---
 exec/install-xtrn.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index 724fb68adb..6845ad6603 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -449,7 +449,7 @@ var xtrn_dirs = fullpath(system.ctrl_dir + "../xtrn/*");
 if(!ini_list.length) {
 	var dir_list = directory(xtrn_dirs);
 	for(var d in dir_list) {
-		if(!options.overwrite && find_startup_dir(dir_list[d]))
+		if(!options.overwrite && find_startup_dir(dir_list[d]) >= 0)
 			continue;
 		var fname = file_getcase(dir_list[d] + ini_fname);
 		if(fname)
-- 
GitLab


From 770acedcc66f66bcdb465ffc227b374f289d232e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 12:32:58 -0700
Subject: [PATCH 664/752] Comment out the default local ANSI library Script
 errors-out if a user selected it and there was no such directory.

---
 xtrn/ansiview/settings.ini | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/xtrn/ansiview/settings.ini b/xtrn/ansiview/settings.ini
index 9271971dc5..d85c03dd30 100644
--- a/xtrn/ansiview/settings.ini
+++ b/xtrn/ansiview/settings.ini
@@ -6,11 +6,11 @@ lbg = BG_CYAN
 sfg = WHITE
 sbg = BG_BLUE
 
-[ANSI Gallery]
-description = A local archive of ANSI and ASCII artwork
-module = local.js
-path = /sbbs/xtrn/ansiview/library/ansi
-hide = *.exe,*.com
+;[ANSI Gallery]
+;description = A local archive of ANSI and ASCII artwork
+;module = local.js
+;path = /sbbs/xtrn/ansiview/library/ansi
+;hide = *.exe,*.com
 
 ;[electronic chicken bbs]
 ;description = An online archive of ANSI and ASCII artwork
-- 
GitLab


From 2ae553e6980f1729db88e714bb64179f5e13568e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 15:20:35 -0700
Subject: [PATCH 665/752] Execute the "termsetup" module after calling the
 user_defaults function.

This was done in the default/classic command shell, but no others.
The termsetup module handles terminal setup tasks that should correspond
with the user's configuration of their terminal preferences (e.g. iCE colors,
alternative fonts). If escape sequences need to be sent to the terminal
server (the BBS) to coordinate the settings, this is the module that does
that.
---
 exec/major.src    | 3 ++-
 exec/pcboard.src  | 3 ++-
 exec/renegade.src | 3 ++-
 exec/sdos.src     | 3 ++-
 exec/simple.src   | 3 ++-
 exec/wildcat.src  | 3 ++-
 exec/wwiv.src     | 1 +
 7 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/exec/major.src b/exec/major.src
index 0365bce932..1e56d75383 100644
--- a/exec/major.src
+++ b/exec/major.src
@@ -2,7 +2,7 @@
 
 # MajorBBS Clone Command Shell for Synchronet
 
-# $Id: major.src,v 1.12 2020/04/23 02:46:08 rswindell Exp $
+# $Id: major.src,v 1.13 2020/09/12 02:46:08 rswindell Exp $
 
 # @format.tab-size 8, @format.use-tabs true
 
@@ -132,6 +132,7 @@ cmdkey A
 
 	cmdkey A
 		user_defaults
+		exec_bin "termsetup"
 		end_cmd
 
 	cmdkey X
diff --git a/exec/pcboard.src b/exec/pcboard.src
index 93dc2b6e5b..d360f3ed65 100644
--- a/exec/pcboard.src
+++ b/exec/pcboard.src
@@ -2,7 +2,7 @@
 
 # PCBoard v15.1 menu/command emulation
 
-# $Id: pcboard.src,v 1.13 2020/04/23 02:46:09 rswindell Exp $
+# $Id: pcboard.src,v 1.14 2020/09/12 02:46:09 rswindell Exp $
 
 # @format.tab-size 8, @format.use-tabs true
 
@@ -219,6 +219,7 @@ cmdstr G
 
 cmdstr W
 	user_defaults
+	exec_bin "termsetup"
 	end_cmd
 
 cmdstr O
diff --git a/exec/renegade.src b/exec/renegade.src
index 53619df9d0..a16b089afb 100644
--- a/exec/renegade.src
+++ b/exec/renegade.src
@@ -1,6 +1,6 @@
 # renegade.src
 
-# $Id: renegade.src,v 1.16 2020/04/23 02:46:09 rswindell Exp $
+# $Id: renegade.src,v 1.16 2020/09/12 02:46:09 rswindell Exp $
 
 # @format.tab-size 8, @format.use-tabs true
 
@@ -155,6 +155,7 @@ cmdkey O
 
 cmdkey P
         user_defaults
+        exec_bin "termsetup"
         end_cmd
 
 cmdkey !
diff --git a/exec/sdos.src b/exec/sdos.src
index 7c568fe29d..bee7966242 100644
--- a/exec/sdos.src
+++ b/exec/sdos.src
@@ -2,7 +2,7 @@
 
 # DOS shell for Synchronet version 2 systems
 
-# $Id: sdos.src,v 1.15 2020/04/23 02:46:09 rswindell Exp $
+# $Id: sdos.src,v 1.15 2020/09/12 02:46:09 rswindell Exp $
 
 # @format.tab-size 8, @format.use-tabs true
 
@@ -149,6 +149,7 @@ if_true
 compare_str "setup"
 if_true
 	user_defaults
+	exec_bin "termsetup"
 	return
 	end_if
 
diff --git a/exec/simple.src b/exec/simple.src
index d9dec4f0f4..00a47bda77 100644
--- a/exec/simple.src
+++ b/exec/simple.src
@@ -2,7 +2,7 @@
 
 # Simple Synchronet Command Shell - for beginner/first-time users
 
-# $Id: simple.src,v 1.13 2020/04/23 02:46:09 rswindell Exp $
+# $Id: simple.src,v 1.13 2020/09/12 02:46:09 rswindell Exp $
 
 # @format.tab-size 8, @format.use-tabs true
 
@@ -187,6 +187,7 @@ cmdstr DOORS
 #Account config (user defaults)
 cmdstr A
 	user_defaults
+	exec_bin "termsetup"
 	end_cmd
 
 #Page another user
diff --git a/exec/wildcat.src b/exec/wildcat.src
index b4c5a2f05c..9a9df8698c 100644
--- a/exec/wildcat.src
+++ b/exec/wildcat.src
@@ -1,6 +1,6 @@
 # wildcat.src
 
-# $Id: wildcat.src,v 1.17 2020/04/23 02:46:09 rswindell Exp $
+# $Id: wildcat.src,v 1.17 2020/09/12 02:46:09 rswindell Exp $
 
 # @format.tab-size 8, @format.use-tabs true
 
@@ -95,6 +95,7 @@ cmdkey I
 
 cmdkey Y
 	user_defaults
+	exec_bin "termsetup"
 	end_cmd
 
 cmdkey W
diff --git a/exec/wwiv.src b/exec/wwiv.src
index 32e1a977ed..230ebc7996 100644
--- a/exec/wwiv.src
+++ b/exec/wwiv.src
@@ -135,6 +135,7 @@ cmdkey C
 
 cmdkey D
 	user_defaults
+	exec_bin "termsetup"
 	end_cmd
 
 cmdkey E
-- 
GitLab


From 62715af75423a35eb4d1d66cb92f3ddd750c9029 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 23:17:38 -0700
Subject: [PATCH 666/752] Make "dark mode" the default sbbsctrl style.

Read and write the Log fonts when importing/exporting sbbsctrl.ini.
Use sbbsctrl*.ini as the default Import Settings file mask.
---
 src/sbbs3/ctrl/ClientFormUnit.dfm   |  3 +-
 src/sbbs3/ctrl/EventsFormUnit.dfm   |  5 +--
 src/sbbs3/ctrl/FtpFormUnit.dfm      |  5 +--
 src/sbbs3/ctrl/MailFormUnit.dfm     |  5 +--
 src/sbbs3/ctrl/MainFormUnit.cpp     | 54 ++++++++++++++++++++---------
 src/sbbs3/ctrl/MainFormUnit.h       |  2 +-
 src/sbbs3/ctrl/NodeFormUnit.dfm     |  3 +-
 src/sbbs3/ctrl/ServicesFormUnit.dfm |  5 +--
 src/sbbs3/ctrl/TelnetFormUnit.dfm   |  5 +--
 src/sbbs3/ctrl/WebFormUnit.dfm      |  5 +--
 10 files changed, 61 insertions(+), 31 deletions(-)

diff --git a/src/sbbs3/ctrl/ClientFormUnit.dfm b/src/sbbs3/ctrl/ClientFormUnit.dfm
index d92199f2d1..fcab73c125 100644
--- a/src/sbbs3/ctrl/ClientFormUnit.dfm
+++ b/src/sbbs3/ctrl/ClientFormUnit.dfm
@@ -9,7 +9,7 @@ object ClientForm: TClientForm
   DragKind = dkDock
   DragMode = dmAutomatic
   Font.Charset = DEFAULT_CHARSET
-  Font.Color = clWindowText
+  Font.Color = clWhite
   Font.Height = -10
   Font.Name = 'MS Sans Serif'
   Font.Style = []
@@ -23,6 +23,7 @@ object ClientForm: TClientForm
     Width = 615
     Height = 307
     Align = alClient
+    Color = clBlack
     Columns = <
       item
         Caption = 'Socket'
diff --git a/src/sbbs3/ctrl/EventsFormUnit.dfm b/src/sbbs3/ctrl/EventsFormUnit.dfm
index b584d24b9d..2791dd5943 100644
--- a/src/sbbs3/ctrl/EventsFormUnit.dfm
+++ b/src/sbbs3/ctrl/EventsFormUnit.dfm
@@ -23,11 +23,12 @@ object EventsForm: TEventsForm
     Width = 543
     Height = 352
     Align = alClient
+    Color = clBlack
     Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
+    Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = []
+    Font.Style = [fsBold]
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/FtpFormUnit.dfm b/src/sbbs3/ctrl/FtpFormUnit.dfm
index 807ca4ad51..d4d50f3abb 100644
--- a/src/sbbs3/ctrl/FtpFormUnit.dfm
+++ b/src/sbbs3/ctrl/FtpFormUnit.dfm
@@ -143,11 +143,12 @@ object FtpForm: TFtpForm
     Width = 509
     Height = 86
     Align = alClient
+    Color = clBlack
     Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
+    Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = []
+    Font.Style = [fsBold]
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/MailFormUnit.dfm b/src/sbbs3/ctrl/MailFormUnit.dfm
index db748b0144..ae4fd58282 100644
--- a/src/sbbs3/ctrl/MailFormUnit.dfm
+++ b/src/sbbs3/ctrl/MailFormUnit.dfm
@@ -146,11 +146,12 @@ object MailForm: TMailForm
     Width = 464
     Height = 86
     Align = alClient
+    Color = clBlack
     Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
+    Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = []
+    Font.Style = [fsBold]
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp
index 8ee17f4972..62c35efa2b 100644
--- a/src/sbbs3/ctrl/MainFormUnit.cpp
+++ b/src/sbbs3/ctrl/MainFormUnit.cpp
@@ -972,7 +972,7 @@ __fastcall TMainForm::TMainForm(TComponent* Owner)
         for(i=LOG_EMERG;i<=LOG_DEBUG;i++) {
             LogFont[i] = new TFont;
             LogFont[i]->Color=LogLevelColor[i];
-            if(i<=LOG_ERR)
+//            if(i <= LOG_ERR)
                 LogFont[i]->Style = TFontStyles()<< fsBold;
         }
     }
@@ -1565,12 +1565,12 @@ int __fastcall TMainForm::PageNum(TPageControl* obj)
 	return(PAGE_LOWERRIGHT);
 }
 TColor __fastcall TMainForm::ReadColor(TRegistry* Registry
-    ,AnsiString name)
+    ,AnsiString name, TColor deflt)
 {
     if(Registry->ValueExists(name + "Color"))
         return(StringToColor(Registry->ReadString(name + "Color")));
         
-    return(clWindow);   // Default
+    return deflt;
 }
 void __fastcall TMainForm::WriteColor(TRegistry* Registry
     ,AnsiString name, TColor color)
@@ -1755,28 +1755,30 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
     if(Registry->ValueExists("WebFormPage"))
     	WebFormPage=Registry->ReadInteger("WebFormPage");
 
-    TelnetForm->Log->Color=ReadColor(Registry,"TelnetLog");
+    TelnetForm->Log->Color=ReadColor(Registry,"TelnetLog",TelnetForm->Log->Color);
     ReadFont("TelnetLog",TelnetForm->Log->Font);
-    EventsForm->Log->Color=ReadColor(Registry,"EventsLog");
+    EventsForm->Log->Color=ReadColor(Registry,"EventsLog",EventsForm->Log->Color);
     ReadFont("EventsLog",EventsForm->Log->Font);
-    ServicesForm->Log->Color=ReadColor(Registry,"ServicesLog");
+    ServicesForm->Log->Color=ReadColor(Registry,"ServicesLog",ServicesForm->Log->Color);
     ReadFont("ServicesLog",ServicesForm->Log->Font);
-    MailForm->Log->Color=ReadColor(Registry,"MailLog");
+    MailForm->Log->Color=ReadColor(Registry,"MailLog",MailForm->Log->Color);
     ReadFont("MailLog",MailForm->Log->Font);
-    FtpForm->Log->Color=ReadColor(Registry,"FtpLog");
+    FtpForm->Log->Color=ReadColor(Registry,"FtpLog",FtpForm->Log->Color);
     ReadFont("FtpLog",FtpForm->Log->Font);
-    WebForm->Log->Color=ReadColor(Registry,"WebLog");
+    WebForm->Log->Color=ReadColor(Registry,"WebLog",WebForm->Log->Color);
     ReadFont("WebLog",WebForm->Log->Font);
-    NodeForm->ListBox->Color=ReadColor(Registry,"NodeList");
+    NodeForm->ListBox->Color=ReadColor(Registry,"NodeList",NodeForm->ListBox->Color);
     ReadFont("NodeList",NodeForm->ListBox->Font);
-    ClientForm->ListView->Color=ReadColor(Registry,"ClientList");
+    ClientForm->ListView->Color=ReadColor(Registry,"ClientList",ClientForm->ListView->Color);
     ReadFont("ClientList",ClientForm->ListView->Font);
 
     {
         int i;
 
-        for(i=LOG_EMERG; i<=LOG_DEBUG; i++)
-            ReadFont("Log" + AnsiString(LogLevelDesc[i]), LogFont[i]);
+		for(i=LOG_EMERG; i<=LOG_DEBUG; i++) {
+			if(i != LOG_INFO)
+				ReadFont("Log" + AnsiString(LogLevelDesc[i]), LogFont[i]);
+		}
     }
 
 	if(Registry->ValueExists("TelnetFormTop"))
@@ -2241,8 +2243,10 @@ void __fastcall TMainForm::SaveRegistrySettings(TObject* Sender)
     {
         int i;
 
-        for(i=LOG_EMERG;i<=LOG_DEBUG;i++)
-            WriteFont("Log" + AnsiString(LogLevelDesc[i]), LogFont[i]);
+        for(i=LOG_EMERG;i<=LOG_DEBUG;i++) {
+			if(i != LOG_INFO)
+				WriteFont("Log" + AnsiString(LogLevelDesc[i]), LogFont[i]);
+		}
     }
 
     Registry->WriteBool("ToolBarVisible",Toolbar->Visible);
@@ -2358,7 +2362,7 @@ void __fastcall TMainForm::ExportFont(TMemIniFile* IniFile, const char* section,
 void __fastcall TMainForm::ImportSettings(TObject* Sender)
 {
     OpenDialog->Filter="Settings files (*.ini)|*.ini|All files|*.*";
-    OpenDialog->FileName=AnsiString(global.ctrl_dir)+"sbbsctrl.ini";
+    OpenDialog->FileName=AnsiString(global.ctrl_dir)+"sbbsctrl*.ini";
     if(!OpenDialog->Execute())
     	return;
 
@@ -2429,6 +2433,15 @@ void __fastcall TMainForm::ImportSettings(TObject* Sender)
                 =IniFile->ReadInteger(section,str,0);
     }
 
+    {
+        int i;
+
+        for(i=LOG_EMERG; i<=LOG_DEBUG; i++) {
+			if(i != LOG_INFO)
+				ImportFont(IniFile, ("Log" + AnsiString(LogLevelDesc[i]) + "Font").c_str(), "", LogFont[i]);
+		}
+    }
+
     section = "SpyTerminal";
 	SpyTerminalWidth=IniFile->ReadInteger(section, "Width", SpyTerminalWidth);
 	SpyTerminalHeight=IniFile->ReadInteger(section, "Height", SpyTerminalHeight);
@@ -2513,6 +2526,15 @@ void __fastcall TMainForm::ExportSettings(TObject* Sender)
     ExportFont(IniFile,section,"LogFont",WebForm->Log->Font);
     IniFile->WriteString(section,"LogColor",ColorToString(WebForm->Log->Color));
 
+    {
+        int i;
+
+        for(i=LOG_EMERG; i<=LOG_DEBUG; i++) {
+			if(i != LOG_INFO)
+				ExportFont(IniFile, ("Log" + AnsiString(LogLevelDesc[i]) + "Font").c_str(), "", LogFont[i]);
+		}
+    }
+
     section = "SpyTerminal";
 	IniFile->WriteInteger(section, "Width"
                             ,SpyTerminalWidth);
diff --git a/src/sbbs3/ctrl/MainFormUnit.h b/src/sbbs3/ctrl/MainFormUnit.h
index 0c1463ede7..e8ec13c10e 100644
--- a/src/sbbs3/ctrl/MainFormUnit.h
+++ b/src/sbbs3/ctrl/MainFormUnit.h
@@ -493,7 +493,7 @@ public:		// User declarations
 	int __fastcall  PageNum(TPageControl* obj);
     void __fastcall FormMinimize(TObject *Sender);
 	void __fastcall DisplayMainPanels(TObject *Sender);
-    TColor __fastcall ReadColor(TRegistry*, AnsiString);
+    TColor __fastcall ReadColor(TRegistry*, AnsiString, TColor);
     void __fastcall WriteColor(TRegistry*, AnsiString, TColor);
     void __fastcall ReadFont(AnsiString, TFont*);
     void __fastcall WriteFont(AnsiString, TFont*);
diff --git a/src/sbbs3/ctrl/NodeFormUnit.dfm b/src/sbbs3/ctrl/NodeFormUnit.dfm
index 9c7ac1d6d8..8541939bdd 100644
--- a/src/sbbs3/ctrl/NodeFormUnit.dfm
+++ b/src/sbbs3/ctrl/NodeFormUnit.dfm
@@ -10,7 +10,7 @@ object NodeForm: TNodeForm
   DragKind = dkDock
   DragMode = dmAutomatic
   Font.Charset = DEFAULT_CHARSET
-  Font.Color = clWindowText
+  Font.Color = clWhite
   Font.Height = -11
   Font.Name = 'MS Sans Serif'
   Font.Style = []
@@ -114,6 +114,7 @@ object NodeForm: TNodeForm
     MultiSelect = True
     PopupMenu = PopupMenu
     TabOrder = 1
+    Color = clBlack
   end
   object Timer: TTimer
     OnTimer = TimerTick
diff --git a/src/sbbs3/ctrl/ServicesFormUnit.dfm b/src/sbbs3/ctrl/ServicesFormUnit.dfm
index 3c1edab46c..016431acbd 100644
--- a/src/sbbs3/ctrl/ServicesFormUnit.dfm
+++ b/src/sbbs3/ctrl/ServicesFormUnit.dfm
@@ -127,11 +127,12 @@ object ServicesForm: TServicesForm
     Width = 721
     Height = 144
     Align = alClient
+    Color = clBlack
     Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
+    Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = []
+    Font.Style = [fsBold]
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/TelnetFormUnit.dfm b/src/sbbs3/ctrl/TelnetFormUnit.dfm
index ef88ca0ba5..f05082bd12 100644
--- a/src/sbbs3/ctrl/TelnetFormUnit.dfm
+++ b/src/sbbs3/ctrl/TelnetFormUnit.dfm
@@ -152,11 +152,12 @@ object TelnetForm: TTelnetForm
     Width = 575
     Height = 150
     Align = alClient
+    Color = clBlack
     Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
+    Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = []
+    Font.Style = [fsBold]
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/WebFormUnit.dfm b/src/sbbs3/ctrl/WebFormUnit.dfm
index 281dca6411..6d74eed36f 100644
--- a/src/sbbs3/ctrl/WebFormUnit.dfm
+++ b/src/sbbs3/ctrl/WebFormUnit.dfm
@@ -148,11 +148,12 @@ object WebForm: TWebForm
     Width = 446
     Height = 165
     Align = alClient
+    Color = clBlack
     Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
+    Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = []
+    Font.Style = [fsBold]
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
-- 
GitLab


From 699d0460f3e8ea3753b779a9470e39e204f13e95 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 23:51:23 -0700
Subject: [PATCH 667/752] Make dark mode the default.

---
 src/sbbs3/ctrl/EventsFormUnit.dfm   | 2 +-
 src/sbbs3/ctrl/FtpFormUnit.dfm      | 2 +-
 src/sbbs3/ctrl/MailFormUnit.dfm     | 2 +-
 src/sbbs3/ctrl/MainFormUnit.cpp     | 6 +++---
 src/sbbs3/ctrl/ServicesFormUnit.dfm | 2 +-
 src/sbbs3/ctrl/TelnetFormUnit.dfm   | 2 +-
 src/sbbs3/ctrl/WebFormUnit.dfm      | 2 +-
 7 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/sbbs3/ctrl/EventsFormUnit.dfm b/src/sbbs3/ctrl/EventsFormUnit.dfm
index 2791dd5943..f816a60970 100644
--- a/src/sbbs3/ctrl/EventsFormUnit.dfm
+++ b/src/sbbs3/ctrl/EventsFormUnit.dfm
@@ -28,7 +28,7 @@ object EventsForm: TEventsForm
     Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = [fsBold]
+    Font.Style = []
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/FtpFormUnit.dfm b/src/sbbs3/ctrl/FtpFormUnit.dfm
index d4d50f3abb..eac7cb770b 100644
--- a/src/sbbs3/ctrl/FtpFormUnit.dfm
+++ b/src/sbbs3/ctrl/FtpFormUnit.dfm
@@ -148,7 +148,7 @@ object FtpForm: TFtpForm
     Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = [fsBold]
+    Font.Style = []
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/MailFormUnit.dfm b/src/sbbs3/ctrl/MailFormUnit.dfm
index ae4fd58282..b1e737ef98 100644
--- a/src/sbbs3/ctrl/MailFormUnit.dfm
+++ b/src/sbbs3/ctrl/MailFormUnit.dfm
@@ -151,7 +151,7 @@ object MailForm: TMailForm
     Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = [fsBold]
+    Font.Style = []
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp
index 62c35efa2b..45e125b249 100644
--- a/src/sbbs3/ctrl/MainFormUnit.cpp
+++ b/src/sbbs3/ctrl/MainFormUnit.cpp
@@ -141,9 +141,9 @@ const TColor LogLevelColor[] = {
                                 ,clRed
                                 ,clRed
                                 ,clFuchsia	
-                                ,clBlue
+                                ,clSkyBlue
                                 ,clBlack    /* not used */
-                                ,clGreen
+                                ,clLime
                                 };
 
 link_list_t bbs_log_list;
@@ -972,7 +972,7 @@ __fastcall TMainForm::TMainForm(TComponent* Owner)
         for(i=LOG_EMERG;i<=LOG_DEBUG;i++) {
             LogFont[i] = new TFont;
             LogFont[i]->Color=LogLevelColor[i];
-//            if(i <= LOG_ERR)
+            if(i <= LOG_CRIT)
                 LogFont[i]->Style = TFontStyles()<< fsBold;
         }
     }
diff --git a/src/sbbs3/ctrl/ServicesFormUnit.dfm b/src/sbbs3/ctrl/ServicesFormUnit.dfm
index 016431acbd..bfaabcf57a 100644
--- a/src/sbbs3/ctrl/ServicesFormUnit.dfm
+++ b/src/sbbs3/ctrl/ServicesFormUnit.dfm
@@ -132,7 +132,7 @@ object ServicesForm: TServicesForm
     Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = [fsBold]
+    Font.Style = []
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/TelnetFormUnit.dfm b/src/sbbs3/ctrl/TelnetFormUnit.dfm
index f05082bd12..b83bfc224d 100644
--- a/src/sbbs3/ctrl/TelnetFormUnit.dfm
+++ b/src/sbbs3/ctrl/TelnetFormUnit.dfm
@@ -157,7 +157,7 @@ object TelnetForm: TTelnetForm
     Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = [fsBold]
+    Font.Style = []
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
diff --git a/src/sbbs3/ctrl/WebFormUnit.dfm b/src/sbbs3/ctrl/WebFormUnit.dfm
index 6d74eed36f..86bc8234fa 100644
--- a/src/sbbs3/ctrl/WebFormUnit.dfm
+++ b/src/sbbs3/ctrl/WebFormUnit.dfm
@@ -153,7 +153,7 @@ object WebForm: TWebForm
     Font.Color = clWhite
     Font.Height = -12
     Font.Name = 'MS Sans Serif'
-    Font.Style = [fsBold]
+    Font.Style = []
     HideScrollBars = False
     ParentFont = False
     PopupMenu = MainForm.LogPopupMenu
-- 
GitLab


From 1a43f2d0698348f9c2570a778a12bde11d733334 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 23:51:50 -0700
Subject: [PATCH 668/752] Make it easy for sysop to switch between light (the
 old default) and dark mode

---
 ctrl/sbbsctrl.darkmode.ini  | 82 +++++++++++++++++++++++++++++++++++++
 ctrl/sbbsctrl.lightmode.ini | 82 +++++++++++++++++++++++++++++++++++++
 2 files changed, 164 insertions(+)
 create mode 100644 ctrl/sbbsctrl.darkmode.ini
 create mode 100644 ctrl/sbbsctrl.lightmode.ini

diff --git a/ctrl/sbbsctrl.darkmode.ini b/ctrl/sbbsctrl.darkmode.ini
new file mode 100644
index 0000000000..cef4df8be4
--- /dev/null
+++ b/ctrl/sbbsctrl.darkmode.ini
@@ -0,0 +1,82 @@
+[NodeForm]
+ListFontName=MS Sans Serif
+ListFontColor=clWhite
+ListFontStyle=0
+ListColor=clBlack
+
+[ClientForm]
+ListFontName=MS Sans Serif
+ListFontColor=clWhite
+ListFontStyle=0
+ListColor=clBlack
+
+[TelnetForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWhite
+LogFontStyle=0
+LogColor=clBlack
+
+[EventsForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWhite
+LogFontStyle=0
+LogColor=clBlack
+
+[ServicesForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWhite
+LogFontStyle=0
+LogColor=clBlack
+
+[FtpForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWhite
+LogFontStyle=0
+LogColor=clBlack
+
+[MailForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWhite
+LogFontStyle=0
+LogColor=clBlack
+
+[WebForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWhite
+LogFontStyle=0
+LogColor=clBlack
+
+[LogEmergencyFont]
+Name=MS Sans Serif
+Color=clRed
+Style=0
+
+[LogAlertFont]
+Name=MS Sans Serif
+Color=clRed
+Style=1
+
+[LogCriticalFont]
+Name=MS Sans Serif
+Color=clRed
+Style=1
+
+[LogErrorFont]
+Name=MS Sans Serif
+Color=clRed
+Style=0
+
+[LogWarningFont]
+Name=MS Sans Serif
+Color=clFuchsia
+Style=0
+
+[LogNoticeFont]
+Name=MS Sans Serif
+Color=clSkyBlue
+Style=0
+
+[LogDebugFont]
+Name=MS Sans Serif
+Color=clLime
+Style=0
diff --git a/ctrl/sbbsctrl.lightmode.ini b/ctrl/sbbsctrl.lightmode.ini
new file mode 100644
index 0000000000..0973b49aa9
--- /dev/null
+++ b/ctrl/sbbsctrl.lightmode.ini
@@ -0,0 +1,82 @@
+[NodeForm]
+ListFontName=MS Sans Serif
+ListFontColor=clWindowText
+ListFontStyle=0
+ListColor=clWindow
+
+[ClientForm]
+ListFontName=MS Sans Serif
+ListFontColor=clWindowText
+ListFontStyle=0
+ListColor=clWindow
+
+[TelnetForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWindowText
+LogFontStyle=0
+LogColor=clWindow
+
+[EventsForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWindowText
+LogFontStyle=0
+LogColor=clWindow
+
+[ServicesForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWindowText
+LogFontStyle=0
+LogColor=clWindow
+
+[FtpForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWindowText
+LogFontStyle=0
+LogColor=clWindow
+
+[MailForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWindowText
+LogFontStyle=0
+LogColor=clWindow
+
+[WebForm]
+LogFontName=MS Sans Serif
+LogFontColor=clWindowText
+LogFontStyle=0
+LogColor=clWindow
+
+[LogEmergencyFont]
+Name=MS Sans Serif
+Color=clRed
+Style=1
+
+[LogAlertFont]
+Name=MS Sans Serif
+Color=clRed
+Style=1
+
+[LogCriticalFont]
+Name=MS Sans Serif
+Color=clRed
+Style=1
+
+[LogErrorFont]
+Name=MS Sans Serif
+Color=clRed
+Style=1
+
+[LogWarningFont]
+Name=MS Sans Serif
+Color=clFuchsia
+Style=0
+
+[LogNoticeFont]
+Name=MS Sans Serif
+Color=clBlue
+Style=0
+
+[LogDebugFont]
+Name=MS Sans Serif
+Color=clGreen
+Style=0
-- 
GitLab


From acb37e7bee1cf5c9b8c75982ff27771975377ddc Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 23:53:15 -0700
Subject: [PATCH 669/752] Automatically switch back from PETSCII to ANSI mode

If the user's terminal was auto-detected as being ANSI, yet they had their
account settings manually-overridden to use PETSCII, automatically switch
back to auto-terminal mode (e.g. with ANSI).
---
 src/sbbs3/logon.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index 8783f4455f..af92047f7b 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -180,6 +180,10 @@ bool sbbs_t::logon()
 	if(useron.misc&AUTOTERM) {
 		useron.misc&=~(ANSI|RIP|PETSCII);
 		useron.misc|=autoterm;
+	} else if((useron.misc&PETSCII) && (autoterm&ANSI)) {
+		// User manually-enabled PETSCII, but they're logging in with an ANSI (auto-detected) terminal
+		useron.misc &= ~PETSCII;
+		useron.misc |= (AUTOTERM | autoterm);
 	}
 
 	if(!chk_ar(cfg.shell[useron.shell]->ar,&useron,&client)) {
-- 
GitLab


From 55e1b746a199779391da14c7a69c7232c42a99e2 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 23:55:29 -0700
Subject: [PATCH 670/752] Use md() to make the temp directory so get consistent
 error-reporting

---
 src/sbbs3/ftpsrvr.c  | 3 +--
 src/sbbs3/mailsrvr.c | 6 ++----
 src/sbbs3/services.c | 6 ++----
 src/sbbs3/websrvr.c  | 6 ++----
 4 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index b2dca8c89d..c498457d5e 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -6124,8 +6124,7 @@ void DLLCALL ftp_server(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		if(!isdir(scfg.temp_dir) && MKDIR(scfg.temp_dir) != 0) {
-			lprintf(LOG_ERR, "Error %d creating temp directory: %s", errno, scfg.temp_dir);
+		if(!md(scfg.temp_dir)) {
 			cleanup(1,__LINE__);
 			break;
 		}
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index e7646f7536..ff6ae69c91 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -6084,13 +6084,11 @@ void DLLCALL mail_server(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		MKDIR(scfg.temp_dir);
-		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
-		if(!isdir(scfg.temp_dir)) {
-			lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
+		if(!md(scfg.temp_dir)) {
 			cleanup(1);
 			return;
 		}
+		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
 
 		/* Parse the mailproc[.host].ini */
 		mailproc_list=NULL;
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index f7ed012847..c7d2a77a78 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -1837,13 +1837,11 @@ void DLLCALL services_thread(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		MKDIR(scfg.temp_dir);
-		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
-		if(!isdir(scfg.temp_dir)) {
-			lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
+		if(!md(scfg.temp_dir)) {
 			cleanup(1);
 			return;
 		}
+		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
 
 		if((t=checktime())!=0) {   /* Check binary time */
 			lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 53619e75de..d2f7b15525 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -7033,13 +7033,11 @@ void DLLCALL web_server(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 		prep_dir(startup->ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
-		MKDIR(scfg.temp_dir);
-		if(!isdir(scfg.temp_dir)) {
-			lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
+		if(!md(scfg.temp_dir)) {
 			cleanup(1);
 			return;
 		}
+		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
 		lprintf(LOG_DEBUG,"Root directory: %s", root_dir);
 		lprintf(LOG_DEBUG,"Error directory: %s", error_dir);
 		lprintf(LOG_DEBUG,"CGI directory: %s", cgi_dir);
-- 
GitLab


From 7205a96311cb17aee45adacfef25354040761111 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 12 Sep 2020 23:58:19 -0700
Subject: [PATCH 671/752] Resolve binkit.js line 322: TypeError:
 bp.remote_addrs is undefined

---
 exec/binkit.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/binkit.js b/exec/binkit.js
index 81953756e5..3b2fecb8a5 100644
--- a/exec/binkit.js
+++ b/exec/binkit.js
@@ -318,7 +318,7 @@ function callout_auth_cb(mode, bp)
 
 	if (!bp.cb_data.binkitpw || bp.cb_data.binkitpw === '-')
 		addrs.push(bp.cb_data.binkit_to_addr);
-	else {
+	else if(bp.remote_addrs !== undefined) {
 		bp.remote_addrs.forEach(function(addr) {
 			if (bp.cb_data.binkitcfg.node[addr] !== undefined) {
 				if (bp.cb_data.binkitcfg.node[addr].pass === bp.cb_data.binkitpw)
-- 
GitLab


From 928eab87d21c00548202343a2bb9106d17037b74 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 00:14:50 -0700
Subject: [PATCH 672/752] Strip control characters (and Ctrl-A sequences) from
 node status

Extended node status can (and often does) contain Ctrl-A sequences.
Strip those characters/sequences before putting into the NodeForm->ListView
lines here.
---
 src/sbbs3/ctrl/NodeFormUnit.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/ctrl/NodeFormUnit.cpp b/src/sbbs3/ctrl/NodeFormUnit.cpp
index 1181b8d80c..e78d86f7ce 100644
--- a/src/sbbs3/ctrl/NodeFormUnit.cpp
+++ b/src/sbbs3/ctrl/NodeFormUnit.cpp
@@ -34,6 +34,7 @@
  ****************************************************************************/
 
 //---------------------------------------------------------------------------
+#include "sbbs.h"
 #include <vcl.h>
 #pragma hdrstop
 #include <io.h>
@@ -129,6 +130,7 @@ void __fastcall TNodeForm::TimerTick(TObject *Sender)
 {
 	static int nodedab;
     char	str[256];
+	char	tmp[128];
 	char	status[128];
     int		i,n,rd,digits=1;
     node_t	node;
@@ -169,11 +171,11 @@ void __fastcall TNodeForm::TimerTick(TObject *Sender)
 
         if(rd!=sizeof(node_t))
         	continue;
-            
-		sprintf(str,"%*d %s"
+
+		safe_snprintf(str, sizeof(str), "%*d %s"
 			,digits
 			,n+1
-			,nodestatus(&MainForm->cfg,&node,status,sizeof(status),n+1));
+			,strip_ctrl(nodestatus(&MainForm->cfg, &node, status, sizeof(status), n+1), tmp));
         AnsiString Str=AnsiString(str);
         if(ListBox->Items->Count<n+1)
         	ListBox->Items->Add(Str);
-- 
GitLab


From a0444fffade3a5ec00b04bb48279898210388bb4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 00:58:00 -0700
Subject: [PATCH 673/752] Change md() to not call lpritnf (which one?!?) as
 this limited its usefulness

... in other non sbbs.dll modules (e.g. *srvr.dll).

So now log error messages elsewhere where appropriate.

DLL-exported and optimized md() a bit.
---
 src/sbbs3/ftpsrvr.c  |  6 +-----
 src/sbbs3/load_cfg.c | 20 ++++++++++----------
 src/sbbs3/mailsrvr.c |  1 +
 src/sbbs3/main.cpp   | 17 +++++++++++++----
 src/sbbs3/sbbs.h     |  3 ++-
 src/sbbs3/services.c |  1 +
 src/sbbs3/websrvr.c  |  1 +
 7 files changed, 29 insertions(+), 20 deletions(-)

diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index c498457d5e..5077391baa 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -6125,15 +6125,11 @@ void DLLCALL ftp_server(void* arg)
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
 		if(!md(scfg.temp_dir)) {
+			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
 			cleanup(1,__LINE__);
 			break;
 		}
 		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
-		if(!isdir(scfg.temp_dir)) {
-			lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
-			cleanup(1,__LINE__);
-			break;
-		}
 
 		if(!startup->max_clients) {
 			startup->max_clients=scfg.sys_nodes;
diff --git a/src/sbbs3/load_cfg.c b/src/sbbs3/load_cfg.c
index a55d1d66c6..1691863a88 100644
--- a/src/sbbs3/load_cfg.c
+++ b/src/sbbs3/load_cfg.c
@@ -326,9 +326,10 @@ void DLLCALL free_text(char* text[])
 /****************************************************************************/
 /* If the directory 'path' doesn't exist, create it.                      	*/
 /****************************************************************************/
-BOOL md(char *inpath)
+BOOL md(const char* inpath)
 {
 	char	path[MAX_PATH+1];
+	char*	p;
 
 	if(inpath[0]==0)
 		return(FALSE);
@@ -336,19 +337,18 @@ BOOL md(char *inpath)
 	SAFECOPY(path,inpath);
 
 	/* Remove trailing '.' if present */
-	if(path[strlen(path)-1]=='.')
-		path[strlen(path)-1]=0;
+	p = lastchar(path);
+	if(*p=='.')
+		*p = '\0';
 
 	/* Remove trailing slash if present */
-	if(path[strlen(path)-1]=='\\' || path[strlen(path)-1]=='/')
-		path[strlen(path)-1]=0;
+	p = lastchar(path);
+	if(*p == '\\' || *p == '/')
+		*p = '\0';
 
 	if(!isdir(path)) {
-		/* lprintf("Creating directory: %s",path); */
-		if(mkpath(path)) {
-			lprintf(LOG_WARNING,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), path);
-			return(FALSE); 
-		} 
+		if(mkpath(path) != 0)
+			return FALSE;
 	}
 	
 	return(TRUE);
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index ff6ae69c91..3a1ff9a983 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -6085,6 +6085,7 @@ void DLLCALL mail_server(void* arg)
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
 		if(!md(scfg.temp_dir)) {
+			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
 			cleanup(1);
 			return;
 		}
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 1556fea37b..d07f8e3f39 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -3312,7 +3312,8 @@ sbbs_t::sbbs_t(ushort node_num, union xp_sockaddr *addr, size_t addr_len, const
 		else
 			SAFECOPY(cfg.temp_dir,"../temp");
     	prep_dir(cfg.ctrl_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
-		md(cfg.temp_dir);
+		if(!md(cfg.temp_dir))
+			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), cfg.temp_dir);
 		if(sd==INVALID_SOCKET) {	/* events thread */
 			if(startup->first_node==1)
 				SAFEPRINTF(path,"%sevent",cfg.temp_dir);
@@ -3540,7 +3541,10 @@ bool sbbs_t::init()
 		return(false);
 	}
 
-	md(cfg.temp_dir);
+	if(!md(cfg.temp_dir)) {
+		lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), cfg.temp_dir);
+		return false;
+	}
 
 	/* Shared NODE files */
 	SAFEPRINTF2(str,"%s%s",cfg.ctrl_dir,"node.dab");
@@ -5119,8 +5123,13 @@ void DLLCALL bbs_thread(void* arg)
 	/* Create missing node directories and dsts.dab files */
 	lprintf(LOG_INFO,"Verifying/creating node directories");
 	for(i=0;i<=scfg.sys_nodes;i++) {
-		if(i)
-			md(scfg.node_path[i-1]);
+		if(i) {
+			if(!md(scfg.node_path[i-1])) {
+				lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.node_path[i-1]);
+				cleanup(1);
+				return;
+			}
+		}
 		SAFEPRINTF(str,"%sdsts.dab",i ? scfg.node_path[i-1] : scfg.ctrl_dir);
 		if(flength(str)<DSTSDABLEN) {
 			if((file=sopen(str,O_WRONLY|O_CREAT|O_APPEND, SH_DENYNO, DEFFILEMODE))==-1) {
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index b4bf668eea..ad52dda442 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -1259,6 +1259,8 @@ extern "C" {
 	DLLEXPORT void		DLLCALL free_text(char* text[]);
 	DLLEXPORT ushort	DLLCALL sys_timezone(scfg_t* cfg);
 	DLLEXPORT char *	DLLCALL prep_dir(const char* base, char* dir, size_t buflen);
+	DLLEXPORT BOOL 		DLLCALL md(const char *path);
+
 
 	/* scfgsave.c */
 	DLLEXPORT BOOL		DLLCALL save_cfg(scfg_t* cfg, int backup_level);
@@ -1502,7 +1504,6 @@ int		pstrcmp(const char **str1, const char **str2);  /* Compares pointers to poi
 int		strsame(const char *str1, const char *str2);	/* Compares number of same chars */
 
 /* load_cfg.c */
-BOOL 	md(char *path);
 char*	prep_code(char *str, const char* prefix);
 
 #ifdef SBBS /* These aren't exported */
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index c7d2a77a78..d2346e18c2 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -1838,6 +1838,7 @@ void DLLCALL services_thread(void* arg)
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
 		if(!md(scfg.temp_dir)) {
+			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
 			cleanup(1);
 			return;
 		}
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index d2f7b15525..d7a10d54b4 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -7034,6 +7034,7 @@ void DLLCALL web_server(void* arg)
 			SAFECOPY(scfg.temp_dir,"../temp");
 		prep_dir(startup->ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
 		if(!md(scfg.temp_dir)) {
+			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
 			cleanup(1);
 			return;
 		}
-- 
GitLab


From 3b0dd501f6ad5eff3b4af5b805e40c1e29d0bce4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 01:02:29 -0700
Subject: [PATCH 674/752] =?UTF-8?q?Resolve=20gcc=20warning:=20enumeration?=
 =?UTF-8?q?=20value=20=E2=80=98NET=5FNONE=E2=80=99=20not=20handled=20in=20?=
 =?UTF-8?q?switch?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/sbbs3/netmail.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/sbbs3/netmail.cpp b/src/sbbs3/netmail.cpp
index bffbe9aad8..65dae51a6b 100644
--- a/src/sbbs3/netmail.cpp
+++ b/src/sbbs3/netmail.cpp
@@ -1445,6 +1445,8 @@ extern "C" BOOL is_supported_netmail_addr(scfg_t* cfg, const char* addr)
 			qwk_route(cfg, p + 1, fulladdr, sizeof(fulladdr)-1);
 			return fulladdr[0] != 0;
 		}
+		default:
+			return FALSE;
 	}
 	return FALSE;
 }
-- 
GitLab


From 53030dd8c67fb41e15644a1640b524ab797a0afb Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 02:48:52 -0700
Subject: [PATCH 675/752] Insure active_clients is initialized before cleanup()
 can be called.

Fix reported and observed crash in cleanup() (in ftp, mail, websrvr)
when failing to create the temp directory. This was due to cleanup()
being called before the protected integer "active_clients" was
initialized.

Also, md() needs to return the errno value (not a BOOL) since the
caller may be in another DLL with a different errno (which likely
has a value of 0/no error).
---
 src/sbbs3/ftpsrvr.c  |  6 +++---
 src/sbbs3/load_cfg.c |  8 ++++----
 src/sbbs3/mailsrvr.c |  8 +++-----
 src/sbbs3/main.cpp   | 13 +++++++------
 src/sbbs3/sbbs.h     |  3 +--
 src/sbbs3/services.c |  4 ++--
 src/sbbs3/websrvr.c  |  9 +++++----
 7 files changed, 25 insertions(+), 26 deletions(-)

diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index 5077391baa..f67899d860 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -6049,6 +6049,7 @@ void DLLCALL ftp_server(void* arg)
 	startup->shutdown_now=FALSE;
 	terminate_server=FALSE;
 	protected_uint32_init(&thread_count, 0);
+	protected_uint32_init(&active_clients, 0);
 
 	do {
 		/* Setup intelligent defaults */
@@ -6124,8 +6125,8 @@ void DLLCALL ftp_server(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		if(!md(scfg.temp_dir)) {
-			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
+		if((i = md(scfg.temp_dir)) != 0) {
+			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", i, strerror(i), scfg.temp_dir);
 			cleanup(1,__LINE__);
 			break;
 		}
@@ -6155,7 +6156,6 @@ void DLLCALL ftp_server(void* arg)
 
 		lprintf(LOG_DEBUG,"Maximum inactivity: %d seconds",startup->max_inactivity);
 
-		protected_uint32_init(&active_clients, 0);
 		update_clients();
 
 		strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */
diff --git a/src/sbbs3/load_cfg.c b/src/sbbs3/load_cfg.c
index 1691863a88..61c69d880b 100644
--- a/src/sbbs3/load_cfg.c
+++ b/src/sbbs3/load_cfg.c
@@ -326,13 +326,13 @@ void DLLCALL free_text(char* text[])
 /****************************************************************************/
 /* If the directory 'path' doesn't exist, create it.                      	*/
 /****************************************************************************/
-BOOL md(const char* inpath)
+int md(const char* inpath)
 {
 	char	path[MAX_PATH+1];
 	char*	p;
 
 	if(inpath[0]==0)
-		return(FALSE);
+		return EINVAL;
 
 	SAFECOPY(path,inpath);
 
@@ -348,10 +348,10 @@ BOOL md(const char* inpath)
 
 	if(!isdir(path)) {
 		if(mkpath(path) != 0)
-			return FALSE;
+			return errno;
 	}
 	
-	return(TRUE);
+	return 0;
 }
 
 /****************************************************************************/
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index 3a1ff9a983..44607a9e5c 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -5867,7 +5867,6 @@ static void cleanup(int code)
 		}
 		lprintf(LOG_INFO, "0000 Done waiting for child threads to terminate");
 	}
-
 	free_cfg(&scfg);
 
 	semfile_list_free(&recycle_semfiles);
@@ -5899,7 +5898,6 @@ static void cleanup(int code)
 	if(WSAInitialized && WSACleanup()!=0) 
 		lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
 #endif
-
 	thread_down();
 	status("Down");
 	if(terminate_server || code) {
@@ -6014,6 +6012,7 @@ void DLLCALL mail_server(void* arg)
 
 	SetThreadName("sbbs/mailServer");
 	protected_uint32_init(&thread_count, 0);
+	protected_uint32_init(&active_clients, 0);
 
 	do {
 		/* Setup intelligent defaults */
@@ -6084,8 +6083,8 @@ void DLLCALL mail_server(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		if(!md(scfg.temp_dir)) {
-			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
+		if((i = md(scfg.temp_dir)) != 0) {
+			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", i, strerror(i), scfg.temp_dir);
 			cleanup(1);
 			return;
 		}
@@ -6149,7 +6148,6 @@ void DLLCALL mail_server(void* arg)
 
 		lprintf(LOG_DEBUG,"Maximum inactivity: %u seconds",startup->max_inactivity);
 
-		protected_uint32_init(&active_clients, 0);
 		update_clients();
 
 		/* open a socket and wait for a client */
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index d07f8e3f39..dc20a334cd 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -3312,8 +3312,8 @@ sbbs_t::sbbs_t(ushort node_num, union xp_sockaddr *addr, size_t addr_len, const
 		else
 			SAFECOPY(cfg.temp_dir,"../temp");
     	prep_dir(cfg.ctrl_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
-		if(!md(cfg.temp_dir))
-			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), cfg.temp_dir);
+		if((i = md(cfg.temp_dir)) != 0)
+			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", i, strerror(i), cfg.temp_dir);
 		if(sd==INVALID_SOCKET) {	/* events thread */
 			if(startup->first_node==1)
 				SAFEPRINTF(path,"%sevent",cfg.temp_dir);
@@ -3541,8 +3541,8 @@ bool sbbs_t::init()
 		return(false);
 	}
 
-	if(!md(cfg.temp_dir)) {
-		lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), cfg.temp_dir);
+	if((i = md(cfg.temp_dir)) != 0) {
+		lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", i, strerror(i), cfg.temp_dir);
 		return false;
 	}
 
@@ -5124,8 +5124,9 @@ void DLLCALL bbs_thread(void* arg)
 	lprintf(LOG_INFO,"Verifying/creating node directories");
 	for(i=0;i<=scfg.sys_nodes;i++) {
 		if(i) {
-			if(!md(scfg.node_path[i-1])) {
-				lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.node_path[i-1]);
+			int err;
+			if((err = md(scfg.node_path[i-1])) != 0) {
+				lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", err, strerror(err), scfg.node_path[i-1]);
 				cleanup(1);
 				return;
 			}
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index ad52dda442..062e974718 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -1259,8 +1259,7 @@ extern "C" {
 	DLLEXPORT void		DLLCALL free_text(char* text[]);
 	DLLEXPORT ushort	DLLCALL sys_timezone(scfg_t* cfg);
 	DLLEXPORT char *	DLLCALL prep_dir(const char* base, char* dir, size_t buflen);
-	DLLEXPORT BOOL 		DLLCALL md(const char *path);
-
+	DLLEXPORT int 		DLLCALL md(const char *path);
 
 	/* scfgsave.c */
 	DLLEXPORT BOOL		DLLCALL save_cfg(scfg_t* cfg, int backup_level);
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index d2346e18c2..f09ba8ee17 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -1837,8 +1837,8 @@ void DLLCALL services_thread(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		if(!md(scfg.temp_dir)) {
-			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
+		if((i = md(scfg.temp_dir)) != 0) {
+			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", i, strerror(i), scfg.temp_dir);
 			cleanup(1);
 			return;
 		}
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index d7a10d54b4..bfabe167b4 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -6914,7 +6914,8 @@ void DLLCALL web_server(void* arg)
 	http_session_t *	session=NULL;
 	void			*acc_type;
 	char			*ssl_estr;
-	int			lvl;
+	int				lvl;
+	int				i;
 
 	startup=(web_startup_t*)arg;
 
@@ -6953,6 +6954,7 @@ void DLLCALL web_server(void* arg)
 	startup->shutdown_now=FALSE;
 	terminate_server=FALSE;
 	protected_uint32_init(&thread_count, 0);
+	protected_uint32_init(&active_clients,0);
 
 	do {
 		/* Setup intelligent defaults */
@@ -7033,8 +7035,8 @@ void DLLCALL web_server(void* arg)
 		else
 			SAFECOPY(scfg.temp_dir,"../temp");
 		prep_dir(startup->ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
-		if(!md(scfg.temp_dir)) {
-			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", errno, strerror(errno), scfg.temp_dir);
+		if((i = md(scfg.temp_dir)) != 0) {
+			lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", i, strerror(i), scfg.temp_dir);
 			cleanup(1);
 			return;
 		}
@@ -7074,7 +7076,6 @@ void DLLCALL web_server(void* arg)
 		if(uptime==0)
 			uptime=time(NULL);	/* this must be done *after* setting the timezone */
 
-		protected_uint32_init(&active_clients,0);
 		update_clients();
 
 		/* open a socket and wait for a client */
-- 
GitLab


From 53339990cff613541a31cb031e9165cb6106c33f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 03:17:06 -0700
Subject: [PATCH 676/752] Fix the startup dir comparison (excluding
 already-installed doors).

---
 exec/install-xtrn.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index 6845ad6603..d9e69df0b9 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -438,18 +438,20 @@ for (var i = 0; i < argc; i++) {
 
 function find_startup_dir(dir)
 {
-	for (var i=0; i < xtrn_area.prog.length; i++) {
+	for (var i in xtrn_area.prog) {
+		if(!xtrn_area.prog[i].startup_dir)
+			continue;
 		if (xtrn_area.prog[i].startup_dir.toLowerCase() == dir.toLowerCase())
 			return i;
 	}
-	return -1;
+	return null;
 }
 
 var xtrn_dirs = fullpath(system.ctrl_dir + "../xtrn/*");
 if(!ini_list.length) {
 	var dir_list = directory(xtrn_dirs);
 	for(var d in dir_list) {
-		if(!options.overwrite && find_startup_dir(dir_list[d]) >= 0)
+		if(!options.overwrite && find_startup_dir(fullpath(dir_list[d])) != null)
 			continue;
 		var fname = file_getcase(dir_list[d] + ini_fname);
 		if(fname)
-- 
GitLab


From eee69489a2e77b193d745294f67db8078a6a6c4f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 12:31:22 -0700
Subject: [PATCH 677/752] Display an error message to the user when attempting
 to view non-existent file

---
 exec/text_sec.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/exec/text_sec.js b/exec/text_sec.js
index 8b30d186e0..53c3599858 100644
--- a/exec/text_sec.js
+++ b/exec/text_sec.js
@@ -185,6 +185,10 @@ while(bbs.online) {
 						alert("Sorry, you can't read that file");
 						break;
 					}
+					if(!list[cmd].path) {
+						alert("Sorry, that file doesn't exist yet");
+						break;
+					}
 					var mode = P_OPENCLOSE | P_CPM_EOF;
 					if(list[cmd].mode !== undefined)
 						mode = eval(list[cmd].mode);
-- 
GitLab


From e15dae4da795b55e4c1624ccd731c1af66ec99a6 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 12:33:57 -0700
Subject: [PATCH 678/752] Fixes to printfile and printtail methods

Throw an exception if no filename is specified (rather than just abort the
script by returning JS_FALSE).

The return value is supposed to be a bool, not an int (JS_TRUE != JSVAL_TRUE).
---
 src/sbbs3/js_console.cpp | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/sbbs3/js_console.cpp b/src/sbbs3/js_console.cpp
index b3c10c9b4a..efc8894b05 100644
--- a/src/sbbs3/js_console.cpp
+++ b/src/sbbs3/js_console.cpp
@@ -1395,6 +1395,11 @@ js_printfile(JSContext *cx, uintN argc, jsval *arglist)
 	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
 		return(JS_FALSE);
 
+	if(JSVAL_NULL_OR_VOID(argv[0])) {
+		JS_ReportError(cx, "No filename specified");
+		return JS_FALSE;
+	}
+
 	str = JS_ValueToString(cx, argv[0]);
 	if (!str)
 		return(JS_FALSE);
@@ -1424,7 +1429,7 @@ js_printfile(JSContext *cx, uintN argc, jsval *arglist)
 	free(cstr);
 	JS_RESUMEREQUEST(cx, rc);
 
-	JS_SET_RVAL(cx, arglist, result ? JS_TRUE : JS_FALSE);
+	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));
 
     return(JS_TRUE);
 }
@@ -1468,8 +1473,10 @@ js_printtail(JSContext *cx, uintN argc, jsval *arglist)
 		}
 	}
 
-	if(js_str==NULL)
-		return(JS_FALSE);
+	if(js_str==NULL) {
+		JS_ReportError(cx, "No filename specified");
+		return JS_FALSE;
+	}
 
 	if(!lines)
 		lines=5;
@@ -1482,7 +1489,7 @@ js_printtail(JSContext *cx, uintN argc, jsval *arglist)
 	free(cstr);
 	JS_RESUMEREQUEST(cx, rc);
 
-	JS_SET_RVAL(cx, arglist, result ? JS_TRUE : JS_FALSE);
+	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));
 
     return(JS_TRUE);
 }
-- 
GitLab


From fad307530c81afd055228a4c1eef32222684489a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 12:36:01 -0700
Subject: [PATCH 679/752] Fix error return value of list method bool
 (JSVAL_FALSE), not number (JS_FALSE).

---
 src/sbbs3/js_uifc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sbbs3/js_uifc.c b/src/sbbs3/js_uifc.c
index cbc39fdd32..4d09ea0ac7 100644
--- a/src/sbbs3/js_uifc.c
+++ b/src/sbbs3/js_uifc.c
@@ -755,7 +755,7 @@ js_uifc_list(JSContext *cx, uintN argc, jsval *arglist)
 		}
 	}
 	if(title == NULL || opts == NULL) {
-		JS_SET_RVAL(cx, arglist, JS_FALSE);
+		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
 	} else {
 		rc=JS_SUSPENDREQUEST(cx);
 		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(uifc->list(mode,left,top,width,(int*)dptr,(int*)bptr,title,opts)));
-- 
GitLab


From ca1decd0a9ca543b1bfddd71b5818a0983e3bb03 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 13:54:38 -0700
Subject: [PATCH 680/752] Fix new crash when recycling introduced in
 3b0dd501f6a

active_clients is destroyed in cleanup(), so it must be re-initialized inside
the server init/recycle loop.
---
 src/sbbs3/ftpsrvr.c  | 3 ++-
 src/sbbs3/mailsrvr.c | 3 ++-
 src/sbbs3/websrvr.c  | 3 ++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index f67899d860..9e9065a678 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -6049,9 +6049,10 @@ void DLLCALL ftp_server(void* arg)
 	startup->shutdown_now=FALSE;
 	terminate_server=FALSE;
 	protected_uint32_init(&thread_count, 0);
-	protected_uint32_init(&active_clients, 0);
 
 	do {
+		protected_uint32_init(&active_clients, 0);
+
 		/* Setup intelligent defaults */
 		if(startup->port==0)					startup->port=IPPORT_FTP;
 		if(startup->qwk_timeout==0)				startup->qwk_timeout=FTP_DEFAULT_QWK_TIMEOUT;		/* seconds */
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index 44607a9e5c..24fd1bd8bd 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -6012,9 +6012,10 @@ void DLLCALL mail_server(void* arg)
 
 	SetThreadName("sbbs/mailServer");
 	protected_uint32_init(&thread_count, 0);
-	protected_uint32_init(&active_clients, 0);
 
 	do {
+		protected_uint32_init(&active_clients, 0);
+
 		/* Setup intelligent defaults */
 		if(startup->relay_port==0)				startup->relay_port=IPPORT_SMTP;
 		if(startup->submission_port==0)			startup->submission_port=IPPORT_SUBMISSION;
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index bfabe167b4..0a7223450d 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -6954,9 +6954,10 @@ void DLLCALL web_server(void* arg)
 	startup->shutdown_now=FALSE;
 	terminate_server=FALSE;
 	protected_uint32_init(&thread_count, 0);
-	protected_uint32_init(&active_clients,0);
 
 	do {
+		protected_uint32_init(&active_clients,0);
+
 		/* Setup intelligent defaults */
 		if(startup->port==0)					startup->port=IPPORT_HTTP;
 		if(startup->root_dir[0]==0)				SAFECOPY(startup->root_dir,WEB_DEFAULT_ROOT_DIR);
-- 
GitLab


From dbc659c6ceccec730ed06e5b33c3350055bbd1a2 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 16:14:26 -0700
Subject: [PATCH 681/752] Display helpful message when denying
 telegram-sent-to-self

Previously, just the NodeNIsNotInUse message would be displayed.
Instead, display the NoNeedToSendMsgToSelf message.
Sysops are free to message themselves.
---
 exec/privatemsg.js | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/exec/privatemsg.js b/exec/privatemsg.js
index 67416ee2e7..02b4222f1e 100644
--- a/exec/privatemsg.js
+++ b/exec/privatemsg.js
@@ -1,4 +1,4 @@
-// $Id: privatemsg.js,v 1.10 2020/04/21 20:30:19 rswindell Exp $
+// $Id: privatemsg.js,v 1.10 2020/09/13 20:30:19 rswindell Exp $
 
 // Private Message Module
 // Installed in SCFG->System->Loadable Modules->Private Msg
@@ -67,8 +67,6 @@ while(bbs.online && !(console.aborted)) {
 					continue;
 				if(node.misc & NODE_POFF)
 					continue;
-				if(node.useron == user.number)
-					continue;
 				if(!shown) {
 					writeln();
 					write(bbs.text(NodeLstHdr));
@@ -81,8 +79,6 @@ while(bbs.online && !(console.aborted)) {
 			var web_users = presence.web_users();
 			for(w = 0; w < web_users.length; w++) {
 				var web_user = web_users[w];
-				if(web_user.name == user.alias)
-					continue;
 				if(web_user.do_not_disturb)
 					continue;
 				if(!shown) {
@@ -103,11 +99,11 @@ while(bbs.online && !(console.aborted)) {
 			var node_num = parseInt(str, 10);
 			var user_num;
 			if(node_num > 0) {
-				if(users[node_num - 1] == undefined) {
+				user_num = users[node_num - 1];
+				if(user_num == undefined) {
 					write(format(bbs.text(NodeNIsNotInUse), node_num));
 					break;
 				}
-				user_num = users[node_num - 1];
 			}
 			else if(str.charAt(0) == '#')
 				user_num = parseInt(str.slice(1), 10);
@@ -122,6 +118,10 @@ while(bbs.online && !(console.aborted)) {
 					break;
 				}
 			}
+			if(user_num == user.number && !user.is_sysop) {
+				write(format(bbs.text(NoNeedToSendMsgToSelf)));
+				break;
+			}
 			var user_name = system.username(user_num);
 			write(format(bbs.text(SendingTelegramToUser), user_name, user_num));
 			var msg = [];
-- 
GitLab


From 929e2bc815ef46e0184465c8503657460466bccd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 16:25:58 -0700
Subject: [PATCH 682/752] Display a nice error message if the file is 0-bytes
 in size.

---
 exec/text_sec.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/exec/text_sec.js b/exec/text_sec.js
index 53c3599858..e98fd432bb 100644
--- a/exec/text_sec.js
+++ b/exec/text_sec.js
@@ -189,6 +189,10 @@ while(bbs.online) {
 						alert("Sorry, that file doesn't exist yet");
 						break;
 					}
+					if(file_size(list[cmd].path) < 1) {
+						alert("Sorry, that file doens't have any content yet");
+						break;
+					}
 					var mode = P_OPENCLOSE | P_CPM_EOF;
 					if(list[cmd].mode !== undefined)
 						mode = eval(list[cmd].mode);
-- 
GitLab


From 0a3cd0994ec9de2a338b84af977fb9527ff36d96 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 16:49:31 -0700
Subject: [PATCH 683/752] List the Web server's (new) HSTS_SAFE option.

---
 ctrl/sbbs.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/ctrl/sbbs.ini b/ctrl/sbbs.ini
index 34fc04c966..a211078726 100644
--- a/ctrl/sbbs.ini
+++ b/ctrl/sbbs.ini
@@ -279,6 +279,7 @@ Options = INDEX_FILE | HTML_INDEX_FILE | ALLOW_QWK
 ;	VIRTUAL_HOSTS
 ;	NO_CGI
 ;	ALLOW_TLS
+;	HSTS_SAFE
 ;	HTTP_LOGGING 
 ; 	NO_HOST_LOOKUP
 ;	NO_RECYCLE
-- 
GitLab


From 3072aaa22641784b8a953e26afa0e570f74ba6bf Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 17:16:13 -0700
Subject: [PATCH 684/752] Add the HSTS Support checkbox so the Web server
 config TLS tab

---
 src/sbbs3/ctrl/WebCfgDlgUnit.cpp |  9 +++++++++
 src/sbbs3/ctrl/WebCfgDlgUnit.dfm | 15 +++++++++++++--
 src/sbbs3/ctrl/WebCfgDlgUnit.h   |  1 +
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/ctrl/WebCfgDlgUnit.cpp b/src/sbbs3/ctrl/WebCfgDlgUnit.cpp
index 708a3700db..b6dc232e1f 100644
--- a/src/sbbs3/ctrl/WebCfgDlgUnit.cpp
+++ b/src/sbbs3/ctrl/WebCfgDlgUnit.cpp
@@ -82,6 +82,10 @@ void __fastcall TWebCfgDlg::FormShow(TObject *Sender)
         TlsEnableCheckBox->Checked = true;
     else
         TlsEnableCheckBox->Checked = false;
+    if(MainForm->web_startup.options&WEB_OPT_HSTS_SAFE)
+        HSTSEnableCheckBox->Checked = true;
+    else
+        HSTSEnableCheckBox->Checked = false;
 
     AuthTypesEdit->Text = AnsiString(MainForm->web_startup.default_auth_list);
     HtmlRootEdit->Text=AnsiString(MainForm->web_startup.root_dir);
@@ -135,6 +139,10 @@ void __fastcall TWebCfgDlg::OKBtnClick(TObject *Sender)
         MainForm->web_startup.options |= WEB_OPT_ALLOW_TLS;
     else
         MainForm->web_startup.options &= ~WEB_OPT_ALLOW_TLS;
+    if(HSTSEnableCheckBox->Checked)
+        MainForm->web_startup.options |= WEB_OPT_HSTS_SAFE;
+    else
+        MainForm->web_startup.options &= ~WEB_OPT_HSTS_SAFE;
 
     SAFECOPY(MainForm->web_startup.default_auth_list
         ,AuthTypesEdit->Text.c_str());
@@ -281,6 +289,7 @@ void __fastcall TWebCfgDlg::TlsEnableCheckBoxClick(TObject *Sender)
     TlsInterfaceLabel->Enabled = enabled;
     TlsPortEdit->Enabled = enabled;
     TlsPortLabel->Enabled = enabled;
+    HSTSEnableCheckBox->Enabled = enabled;
 }
 //---------------------------------------------------------------------------
 
diff --git a/src/sbbs3/ctrl/WebCfgDlgUnit.dfm b/src/sbbs3/ctrl/WebCfgDlgUnit.dfm
index d2c6743199..2f1cac2676 100644
--- a/src/sbbs3/ctrl/WebCfgDlgUnit.dfm
+++ b/src/sbbs3/ctrl/WebCfgDlgUnit.dfm
@@ -25,8 +25,8 @@ object WebCfgDlg: TWebCfgDlg
     Top = 3
     Width = 278
     Height = 199
-    ActivePage = HttpTabSheet
-    TabIndex = 2
+    ActivePage = TlsTabSheet
+    TabIndex = 1
     TabOrder = 0
     object GeneralTabSheet: TTabSheet
       Caption = 'General'
@@ -200,6 +200,17 @@ object WebCfgDlg: TWebCfgDlg
         ShowHint = True
         TabOrder = 2
       end
+      object HSTSEnableCheckBox: TCheckBox
+        Left = 148
+        Top = 10
+        Width = 119
+        Height = 20
+        Hint = 'HTTP Strict Transport Security'
+        Caption = 'HSTS Support'
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 3
+      end
     end
     object HttpTabSheet: TTabSheet
       Caption = 'HTTP'
diff --git a/src/sbbs3/ctrl/WebCfgDlgUnit.h b/src/sbbs3/ctrl/WebCfgDlgUnit.h
index c0979f9a2d..1b4083111d 100644
--- a/src/sbbs3/ctrl/WebCfgDlgUnit.h
+++ b/src/sbbs3/ctrl/WebCfgDlgUnit.h
@@ -108,6 +108,7 @@ __published:	// IDE-managed Components
     TEdit *TlsPortEdit;
     TLabel *AuthTypesLabel;
     TEdit *AuthTypesEdit;
+    TCheckBox *HSTSEnableCheckBox;
     void __fastcall FormShow(TObject *Sender);
     void __fastcall AnswerSoundButtonClick(TObject *Sender);
     void __fastcall HangupSoundButtonClick(TObject *Sender);
-- 
GitLab


From 84e5289d2f9d088442f548d67b462892ccc2f0bf Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 18:19:42 -0700
Subject: [PATCH 685/752] Add system.notify() method for notifying
 user/operator of an important event.

---
 src/sbbs3/js_system.c | 61 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c
index e0e5620aa7..fe45f2c99e 100644
--- a/src/sbbs3/js_system.c
+++ b/src/sbbs3/js_system.c
@@ -1589,6 +1589,59 @@ js_put_telegram(JSContext *cx, uintN argc, jsval *arglist)
 	return(JS_TRUE);
 }
 
+static JSBool
+js_notify(JSContext *cx, uintN argc, jsval *arglist)
+{
+	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
+	jsval *argv=JS_ARGV(cx, arglist);
+	int32		usernumber=1;
+	JSString*	js_subj;
+	JSString*	js_msg;
+	char*		subj;
+	char*		msg = NULL;
+	jsrefcount	rc;
+	BOOL		ret;
+
+	JS_SET_RVAL(cx, arglist, JSVAL_VOID);
+
+	js_system_private_t* sys;
+	if((sys = (js_system_private_t*)js_GetClassPrivate(cx,obj,&js_system_class))==NULL)
+		return JS_FALSE;
+
+	JS_ValueToInt32(cx,argv[0],&usernumber);
+	if(usernumber<1)
+		usernumber=1;
+
+	if((js_subj=JS_ValueToString(cx, argv[1]))==NULL) 
+		return JS_FALSE;
+
+	if(argc > 2) {
+		if((js_msg=JS_ValueToString(cx, argv[2]))==NULL) 
+			return JS_FALSE;
+
+		JSSTRING_TO_MSTRING(cx, js_msg, msg, NULL);
+		HANDLE_PENDING(cx, msg);
+		if(msg==NULL)
+			return JS_TRUE;
+	}
+
+	JSSTRING_TO_MSTRING(cx, js_subj, subj, NULL);
+	HANDLE_PENDING(cx,subj);
+	if(subj==NULL) {
+		free(msg);
+		return JS_TRUE;
+	}
+
+	rc=JS_SUSPENDREQUEST(cx);
+	ret=notify(sys->cfg, usernumber, subj, msg)==0;
+	free(subj);
+	free(msg);
+	JS_RESUMEREQUEST(cx, rc);
+	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
+
+	return JS_TRUE;
+}
+
 static JSBool
 js_new_user(JSContext *cx, uintN argc, jsval *arglist)
 {
@@ -1905,7 +1958,7 @@ js_killpid(JSContext *cx, uintN argc, jsval *arglist)
 
 static jsSyncMethodSpec js_system_functions[] = {
 #ifndef JSDOOR
-	{"username",		js_username,		1,	JSTYPE_STRING,	JSDOCSTR("number")
+	{"username",		js_username,		1,	JSTYPE_STRING,	JSDOCSTR("user_number")
 	,JSDOCSTR("returns name of user in specified user record <i>number</i>, or empty string if not found")
 	,311
 	},
@@ -1986,6 +2039,10 @@ static jsSyncMethodSpec js_system_functions[] = {
 	,JSDOCSTR("sends a user a short text message, delivered immediately or during next logon")
 	,310
 	},		
+	{"notify",			js_notify,			2,	JSTYPE_BOOLEAN,	JSDOCSTR("user_number, subject [,message_text]")
+	,JSDOCSTR("notify a user or operator via both email and a short text message about an important event")
+	,31801
+	},
 	{"newuser",			js_new_user,		1,	JSTYPE_ALIAS },
 	{"new_user",		js_new_user,		1,	JSTYPE_OBJECT,	JSDOCSTR("name/alias [,client object]")
 	,JSDOCSTR("creates a new user record, returns a new <a href=#User>User</a> object representing the new user account, on success.<br>"
@@ -1993,7 +2050,7 @@ static jsSyncMethodSpec js_system_functions[] = {
 	"client object is used if the argument is omitted)")
 	,310
 	},
-	{"del_user",		js_del_user,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("number")
+	{"del_user",		js_del_user,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("user_number")
 	,JSDOCSTR("delete the specified user account")
 	,316
 	},
-- 
GitLab


From 67eab9d02a80bc94c8f93559311bb035a00825dd Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 20:06:46 -0700
Subject: [PATCH 686/752] Don't prompt UTF-8 terminal users to ask if they
 support CP437.

---
 src/sbbs3/useredit.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp
index 74857c0003..ee055e4cf6 100644
--- a/src/sbbs3/useredit.cpp
+++ b/src/sbbs3/useredit.cpp
@@ -969,7 +969,7 @@ void sbbs_t::maindflts(user_t* user)
 				if(sys_status&SS_ABORT)
 					break;
 				if(!(term&PETSCII)) {
-					if(!(user->misc&UTF8) && !yesno(text[ExAsciiTerminalQ]))
+					if(!(term&UTF8) && !yesno(text[ExAsciiTerminalQ]))
 						user->misc|=NO_EXASCII;
 					else
 						user->misc&=~NO_EXASCII;
-- 
GitLab


From 6379674dd530db52b5be611b51dc437b5a770c43 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 21:52:13 -0700
Subject: [PATCH 687/752] Modern web browsers don't render files received via
 FTP, So don't bother linking to 00index*, just the ftp root dir should work
 (for a while yet, anyway)

---
 web/lib/leftnav_html.ssjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/lib/leftnav_html.ssjs b/web/lib/leftnav_html.ssjs
index 16e6356586..4b750f0afb 100644
--- a/web/lib/leftnav_html.ssjs
+++ b/web/lib/leftnav_html.ssjs
@@ -16,7 +16,7 @@
     template.ftp_url += host; 
     if(ftp_port!=21)
         template.ftp_url += ftp_port;
-    template.ftpidx ="/00index.html?$" + new Date().valueOf().toString(36);
+    template.ftpidx ="/";
     template.ftpqwk = "/" + system.qwk_id.toLowerCase() + ".qwk";
  }
 
-- 
GitLab


From bdf26a0a5feadc6bfead68dc462cde847adbf8d0 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 13 Sep 2020 22:57:35 -0700
Subject: [PATCH 688/752] Reset to attributes to normal (lightgray, black bg)
 before executing xtrns

---
 exec/xtrn_sec.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/exec/xtrn_sec.js b/exec/xtrn_sec.js
index e17755ab93..55d7f156b8 100644
--- a/exec/xtrn_sec.js
+++ b/exec/xtrn_sec.js
@@ -82,6 +82,7 @@ function sort_by_name(a, b)
 
 function exec_xtrn(prog)
 {
+	console.attributes = LIGHTGRAY;
 	if(options.clear_screen_on_exec)
 		console.clear();
 	if(options.eval_before_exec)
-- 
GitLab


From 27c279d0ecf3b071cf6f6179ada233856f341061 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 14 Sep 2020 19:27:42 -0700
Subject: [PATCH 689/752] In Windows build, we need/use that NAME argument in
 the usage output.

---
 src/sexpots/sexpots.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/sexpots/sexpots.c b/src/sexpots/sexpots.c
index 3866f571ad..f46b39b47d 100644
--- a/src/sexpots/sexpots.c
+++ b/src/sexpots/sexpots.c
@@ -153,6 +153,9 @@ int usage(const char* fname)
 #endif
 		"\n"
 		,getfname(fname)
+#if defined(_WIN32)
+		,NAME
+#endif
 		);
 
 	return 0;
-- 
GitLab


From 74f629b08f4aa4960bf5e8fc7d4889e718cce161 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 14 Sep 2020 19:52:21 -0700
Subject: [PATCH 690/752] Disable the multi-file upload prompt for the default
 xfer dirs.

---
 ctrl/file.cnf | Bin 12424 -> 12424 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/ctrl/file.cnf b/ctrl/file.cnf
index 3c9957aa695aea170eb2c4624a4426fd8aafeeb7..a829db1df6deec0997627d8fca1e5f46910de550 100644
GIT binary patch
delta 197
zcmeB3>`2_8Cc<=*W1^t`WCdyS&3z)b8Iiab#Zb5+k}&RO1!*BBB!#nNVKQK2?#iQZ
n6_il8$toz^Wojtgml{Z1i_N;)w-7F}*j%8C#5J9~LH`B-Z=OHE

delta 197
zcmeB3>`2_8Cc<=<W1^t`WCdyS&3z)b8Iiab#Zb5+k}&RO1!*BBB!#nNVKQK2?#iQZ
n6_il8$toz^Wojtgml{Z1i_N;)w-7F}*j%8C#5J9~LH`B-gi1g3

-- 
GitLab


From e5ad85a8757f397210fffc27af4eb29210ba750f Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 14 Sep 2020 19:53:32 -0700
Subject: [PATCH 691/752] Switch from static C runtime library to runtime DLL

This fixes the stat() issue on Windows XP/2K3 by allowing sbbs to benefit
from the run-time library updates that Microsoft releases periodically
(like https://www.microsoft.com/en-us/download/details.aspx?id=53840).

For more info on the stat() issue which caused all kinds of sbbs errors
(e.g. creating directories initially, but a lot more):
https://stackoverflow.com/questions/32452777/visual-c-2015-express-stat-not-working-on-windows-xp

Since we are using cryptlib which requires the MSVC runtime DLL anyway
(it is the default build behavior of MSVC), we weren't really gaining
anything from statically linking the CRTL (LIBCMT.LIB). And for some reason,
an up-to-date MSVC2019 still has (links-in) a LIBCMT.LIB file that includes
this stat bug. All the online help resources I found just to seem to suggest
updating the CRTL DLLs (on the target system), with no mention of any fixes
available for the static CRTLs on the build system. But with the no gain
from static linking anyway, I figured it was time to switch to DLL CRTLs.

The debug builds are still statically linking the CRTL for no particular
reason.
---
 src/conio/conio.vcxproj     |   2 +-
 src/conio/conio_sdl.vcxproj |   2 +-
 src/sbbs3/addfiles.vcxproj  |   2 +-
 src/sbbs3/allusers.vcxproj  |   2 +-
 src/sbbs3/ans2asc.vcxproj   |   2 +-
 src/sbbs3/asc2ans.vcxproj   |   2 +-
 src/sbbs3/baja.vcxproj      |   2 +-
 src/sbbs3/chksmb.vcxproj    |   2 +-
 src/sbbs3/ctrl/sbbsctrl.res | Bin 1636 -> 1636 bytes
 src/sbbs3/delfiles.vcxproj  |   2 +-
 src/sbbs3/dstsedit.vcxproj  |   2 +-
 src/sbbs3/dupefind.vcxproj  |   2 +-
 src/sbbs3/echocfg.vcxproj   |   2 +-
 src/sbbs3/filelist.vcxproj  |   2 +-
 src/sbbs3/fixsmb.vcxproj    |   2 +-
 src/sbbs3/ftpsrvr.vcxproj   |   2 +-
 src/sbbs3/jsexec.vcxproj    |   2 +-
 src/sbbs3/load_cfg.vcxproj  |   2 +-
 src/sbbs3/mailsrvr.vcxproj  |   2 +-
 src/sbbs3/makeuser.vcxproj  |   2 +-
 src/sbbs3/node.vcxproj      |   2 +-
 src/sbbs3/ntsvcs.vcxproj    |   2 +-
 src/sbbs3/qwknodes.vcxproj  |   2 +-
 src/sbbs3/sbbs.vcxproj      |   2 +-
 src/sbbs3/sbbscon.vcxproj   |   2 +-
 src/sbbs3/sbbsecho.vcxproj  |   2 +-
 src/sbbs3/scfg/scfg.vcxproj |   2 +-
 src/sbbs3/services.vcxproj  |   2 +-
 src/sbbs3/sexyz.vcxproj     |   2 +-
 src/sbbs3/slog.vcxproj      |   2 +-
 src/sbbs3/smbactiv.vcxproj  |   2 +-
 src/sbbs3/smbutil.vcxproj   |   2 +-
 src/sbbs3/textgen.vcxproj   |   2 +-
 src/sbbs3/unbaja.vcxproj    |   2 +-
 src/sbbs3/websrvr.vcxproj   |   2 +-
 src/sexpots/sexpots.vcxproj |   2 +-
 src/smblib/smblib.vcxproj   |   2 +-
 src/uifc/uifc.vcxproj       |   2 +-
 src/xpdev/xpdev.vcxproj     |   2 +-
 src/xpdev/xpdev_mt.vcxproj  |   2 +-
 40 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/src/conio/conio.vcxproj b/src/conio/conio.vcxproj
index fcbbd146f0..7de5458969 100644
--- a/src/conio/conio.vcxproj
+++ b/src/conio/conio.vcxproj
@@ -65,7 +65,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;LINK_LIST_THREADSAFE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release_mt/conio.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release_mt/</AssemblerListingLocation>
diff --git a/src/conio/conio_sdl.vcxproj b/src/conio/conio_sdl.vcxproj
index 5459079086..72fd54b0b2 100644
--- a/src/conio/conio_sdl.vcxproj
+++ b/src/conio/conio_sdl.vcxproj
@@ -61,7 +61,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;LINK_LIST_THREADSAFE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.sdl.release/conio.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.sdl.release/</AssemblerListingLocation>
diff --git a/src/sbbs3/addfiles.vcxproj b/src/sbbs3/addfiles.vcxproj
index 0ea3f8cfec..941a6ebf50 100644
--- a/src/sbbs3/addfiles.vcxproj
+++ b/src/sbbs3/addfiles.vcxproj
@@ -70,7 +70,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WRAPPER_EXPORTS;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\addfiles/addfiles.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\addfiles/</AssemblerListingLocation>
diff --git a/src/sbbs3/allusers.vcxproj b/src/sbbs3/allusers.vcxproj
index 32aeb52294..a3d20313be 100644
--- a/src/sbbs3/allusers.vcxproj
+++ b/src/sbbs3/allusers.vcxproj
@@ -111,7 +111,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\allusers/allusers.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\allusers/</AssemblerListingLocation>
diff --git a/src/sbbs3/ans2asc.vcxproj b/src/sbbs3/ans2asc.vcxproj
index ec3f4197b5..03e8fc044e 100644
--- a/src/sbbs3/ans2asc.vcxproj
+++ b/src/sbbs3/ans2asc.vcxproj
@@ -62,7 +62,7 @@
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\ans2asc/ans2asc.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\ans2asc/</AssemblerListingLocation>
diff --git a/src/sbbs3/asc2ans.vcxproj b/src/sbbs3/asc2ans.vcxproj
index a7bde1d7b8..ef6e52b3b8 100644
--- a/src/sbbs3/asc2ans.vcxproj
+++ b/src/sbbs3/asc2ans.vcxproj
@@ -103,7 +103,7 @@
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\asc2ans/asc2ans.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\asc2ans/</AssemblerListingLocation>
diff --git a/src/sbbs3/baja.vcxproj b/src/sbbs3/baja.vcxproj
index 8e231451eb..3070658414 100644
--- a/src/sbbs3/baja.vcxproj
+++ b/src/sbbs3/baja.vcxproj
@@ -69,7 +69,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\baja/baja.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\baja/</AssemblerListingLocation>
diff --git a/src/sbbs3/chksmb.vcxproj b/src/sbbs3/chksmb.vcxproj
index 6eb59c21a1..7a678387c3 100644
--- a/src/sbbs3/chksmb.vcxproj
+++ b/src/sbbs3/chksmb.vcxproj
@@ -69,7 +69,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\chksmb/chksmb.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\chksmb/</AssemblerListingLocation>
diff --git a/src/sbbs3/ctrl/sbbsctrl.res b/src/sbbs3/ctrl/sbbsctrl.res
index bf7879314206c403f867e46b71cb130faa5fe8d0..9df7b5e30a44d684ccdebf860a204e457e37f41d 100644
GIT binary patch
delta 35
rcmaFD^Mq%^A!bfS1|bG!1_lPk&8L}_85s>Xzh}{9WK5bY%jOOMrIZMP

delta 35
rcmaFD^Mq%^A!ben1|bG!1_lO(&8L}_85s>Wzh}{9WK5ha%jOOMrBw)h

diff --git a/src/sbbs3/delfiles.vcxproj b/src/sbbs3/delfiles.vcxproj
index 82c1e36acc..5b201b9f58 100644
--- a/src/sbbs3/delfiles.vcxproj
+++ b/src/sbbs3/delfiles.vcxproj
@@ -71,7 +71,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\delfiles/delfiles.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\delfiles/</AssemblerListingLocation>
diff --git a/src/sbbs3/dstsedit.vcxproj b/src/sbbs3/dstsedit.vcxproj
index 793a709e1e..d9caf5befa 100644
--- a/src/sbbs3/dstsedit.vcxproj
+++ b/src/sbbs3/dstsedit.vcxproj
@@ -71,7 +71,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;NO_SOCKET_SUPPORT;SBBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\dstsedit/dstsedit.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\dstsedit/</AssemblerListingLocation>
diff --git a/src/sbbs3/dupefind.vcxproj b/src/sbbs3/dupefind.vcxproj
index bc2b7e1e2b..daa7e3d2c0 100644
--- a/src/sbbs3/dupefind.vcxproj
+++ b/src/sbbs3/dupefind.vcxproj
@@ -112,7 +112,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\dupefind/dupefind.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\dupefind/</AssemblerListingLocation>
diff --git a/src/sbbs3/echocfg.vcxproj b/src/sbbs3/echocfg.vcxproj
index 8af5ed3438..9cdd52a70f 100644
--- a/src/sbbs3/echocfg.vcxproj
+++ b/src/sbbs3/echocfg.vcxproj
@@ -116,7 +116,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>SBBS_EXPORTS;NDEBUG;WIN32;_CONSOLE;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\echocfg/echocfg.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\echocfg/</AssemblerListingLocation>
diff --git a/src/sbbs3/filelist.vcxproj b/src/sbbs3/filelist.vcxproj
index e21587f158..4b662e6cc4 100644
--- a/src/sbbs3/filelist.vcxproj
+++ b/src/sbbs3/filelist.vcxproj
@@ -71,7 +71,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;WRAPPER_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\filelist/filelist.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\filelist/</AssemblerListingLocation>
diff --git a/src/sbbs3/fixsmb.vcxproj b/src/sbbs3/fixsmb.vcxproj
index c052dc3c60..8553232db5 100644
--- a/src/sbbs3/fixsmb.vcxproj
+++ b/src/sbbs3/fixsmb.vcxproj
@@ -110,7 +110,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\fixsmb/fixsmb.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\fixsmb/</AssemblerListingLocation>
diff --git a/src/sbbs3/ftpsrvr.vcxproj b/src/sbbs3/ftpsrvr.vcxproj
index 646d1d20a1..e5e655e12a 100644
--- a/src/sbbs3/ftpsrvr.vcxproj
+++ b/src/sbbs3/ftpsrvr.vcxproj
@@ -81,7 +81,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;_USRDLL;FTPSRVR_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\ftpsrvr/ftpsrvr.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\ftpsrvr/</AssemblerListingLocation>
diff --git a/src/sbbs3/jsexec.vcxproj b/src/sbbs3/jsexec.vcxproj
index 7dc827aa29..e4f3aece34 100644
--- a/src/sbbs3/jsexec.vcxproj
+++ b/src/sbbs3/jsexec.vcxproj
@@ -79,7 +79,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\jsexec/jsexec.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\jsexec/</AssemblerListingLocation>
diff --git a/src/sbbs3/load_cfg.vcxproj b/src/sbbs3/load_cfg.vcxproj
index afb763fe2d..ff742a99da 100644
--- a/src/sbbs3/load_cfg.vcxproj
+++ b/src/sbbs3/load_cfg.vcxproj
@@ -86,7 +86,7 @@
       <SDLCheck>true</SDLCheck>
       <ConformanceMode>false</ConformanceMode>
       <PreprocessorDefinitions>SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
diff --git a/src/sbbs3/mailsrvr.vcxproj b/src/sbbs3/mailsrvr.vcxproj
index 348a1fd9f0..efe827a702 100644
--- a/src/sbbs3/mailsrvr.vcxproj
+++ b/src/sbbs3/mailsrvr.vcxproj
@@ -132,7 +132,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;_USRDLL;MAILSRVR_EXPORTS;SMB_IMPORTS;MD5_IMPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\mailsrvr/mailsrvr.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\mailsrvr/</AssemblerListingLocation>
diff --git a/src/sbbs3/makeuser.vcxproj b/src/sbbs3/makeuser.vcxproj
index e80dbdce8c..8588ea262e 100644
--- a/src/sbbs3/makeuser.vcxproj
+++ b/src/sbbs3/makeuser.vcxproj
@@ -112,7 +112,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\makeuser/makeuser.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\makeuser/</AssemblerListingLocation>
diff --git a/src/sbbs3/node.vcxproj b/src/sbbs3/node.vcxproj
index 13d20e7fcb..3622197890 100644
--- a/src/sbbs3/node.vcxproj
+++ b/src/sbbs3/node.vcxproj
@@ -69,7 +69,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\node/node.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\node/</AssemblerListingLocation>
diff --git a/src/sbbs3/ntsvcs.vcxproj b/src/sbbs3/ntsvcs.vcxproj
index 6ce9bf2c6d..2bf877f9fa 100644
--- a/src/sbbs3/ntsvcs.vcxproj
+++ b/src/sbbs3/ntsvcs.vcxproj
@@ -118,7 +118,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBSNTSVCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\ntsvcs/ntsvcs.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\ntsvcs/</AssemblerListingLocation>
diff --git a/src/sbbs3/qwknodes.vcxproj b/src/sbbs3/qwknodes.vcxproj
index f366337b5f..3cc65b5fc5 100644
--- a/src/sbbs3/qwknodes.vcxproj
+++ b/src/sbbs3/qwknodes.vcxproj
@@ -71,7 +71,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\qwknodes/qwknodes.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\qwknodes/</AssemblerListingLocation>
diff --git a/src/sbbs3/sbbs.vcxproj b/src/sbbs3/sbbs.vcxproj
index 9f934f808b..e2f04731b4 100644
--- a/src/sbbs3/sbbs.vcxproj
+++ b/src/sbbs3/sbbs.vcxproj
@@ -131,7 +131,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;_USRDLL;SBBS;SBBS_EXPORTS;SMB_EXPORTS;RINGBUF_SEM;RINGBUF_MUTEX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\sbbs/sbbs.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\sbbs/</AssemblerListingLocation>
diff --git a/src/sbbs3/sbbscon.vcxproj b/src/sbbs3/sbbscon.vcxproj
index 6ceaf3842e..38f81f19af 100644
--- a/src/sbbs3/sbbscon.vcxproj
+++ b/src/sbbs3/sbbscon.vcxproj
@@ -115,7 +115,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\sbbscon/sbbscon.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\sbbscon/</AssemblerListingLocation>
diff --git a/src/sbbs3/sbbsecho.vcxproj b/src/sbbs3/sbbsecho.vcxproj
index e6bec9bbe6..9ddf9f7968 100644
--- a/src/sbbs3/sbbsecho.vcxproj
+++ b/src/sbbs3/sbbsecho.vcxproj
@@ -71,7 +71,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\sbbsecho/sbbsecho.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\sbbsecho/</AssemblerListingLocation>
diff --git a/src/sbbs3/scfg/scfg.vcxproj b/src/sbbs3/scfg/scfg.vcxproj
index 12d111249f..97e11a629b 100644
--- a/src/sbbs3/scfg/scfg.vcxproj
+++ b/src/sbbs3/scfg/scfg.vcxproj
@@ -117,7 +117,7 @@
       <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SCFG;SBBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\scfg/scfg.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\scfg/</AssemblerListingLocation>
diff --git a/src/sbbs3/services.vcxproj b/src/sbbs3/services.vcxproj
index 5fb6b0a8c8..dc71b1a2f3 100644
--- a/src/sbbs3/services.vcxproj
+++ b/src/sbbs3/services.vcxproj
@@ -131,7 +131,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;_USRDLL;SERVICES_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\services/services.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\services/</AssemblerListingLocation>
diff --git a/src/sbbs3/sexyz.vcxproj b/src/sbbs3/sexyz.vcxproj
index d36a1e5ce1..54532b1a37 100644
--- a/src/sbbs3/sexyz.vcxproj
+++ b/src/sbbs3/sexyz.vcxproj
@@ -112,7 +112,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;RINGBUF_SEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\sexyz/sexyz.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\sexyz/</AssemblerListingLocation>
diff --git a/src/sbbs3/slog.vcxproj b/src/sbbs3/slog.vcxproj
index 5213af3474..fad7a7d1c6 100644
--- a/src/sbbs3/slog.vcxproj
+++ b/src/sbbs3/slog.vcxproj
@@ -109,7 +109,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\slog/slog.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\slog/</AssemblerListingLocation>
diff --git a/src/sbbs3/smbactiv.vcxproj b/src/sbbs3/smbactiv.vcxproj
index bb21030abd..c194623e12 100644
--- a/src/sbbs3/smbactiv.vcxproj
+++ b/src/sbbs3/smbactiv.vcxproj
@@ -112,7 +112,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;NO_SOCKET_SUPPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\smbactiv/smbactiv.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\smbactiv/</AssemblerListingLocation>
diff --git a/src/sbbs3/smbutil.vcxproj b/src/sbbs3/smbutil.vcxproj
index 8d6e60b323..2db9a51596 100644
--- a/src/sbbs3/smbutil.vcxproj
+++ b/src/sbbs3/smbutil.vcxproj
@@ -71,7 +71,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;SBBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\smbutil/smbutil.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\smbutil/</AssemblerListingLocation>
diff --git a/src/sbbs3/textgen.vcxproj b/src/sbbs3/textgen.vcxproj
index 86ecc88d2b..91c125b6c7 100644
--- a/src/sbbs3/textgen.vcxproj
+++ b/src/sbbs3/textgen.vcxproj
@@ -119,7 +119,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>WIN32;SBBS_EXPORTS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\textgen/textgen.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\textgen/</AssemblerListingLocation>
diff --git a/src/sbbs3/unbaja.vcxproj b/src/sbbs3/unbaja.vcxproj
index b1119bc3eb..1ade29c6c2 100644
--- a/src/sbbs3/unbaja.vcxproj
+++ b/src/sbbs3/unbaja.vcxproj
@@ -110,7 +110,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\unbaja/unbaja.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\unbaja/</AssemblerListingLocation>
diff --git a/src/sbbs3/websrvr.vcxproj b/src/sbbs3/websrvr.vcxproj
index 760d454c09..8fd13ace4d 100644
--- a/src/sbbs3/websrvr.vcxproj
+++ b/src/sbbs3/websrvr.vcxproj
@@ -83,7 +83,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;_USRDLL;WEBSRVR_EXPORTS;RINGBUF_SEM;RINGBUF_MUTEX;RINGBUF_EVENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release\websrvr/websrvr.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release\websrvr/</AssemblerListingLocation>
diff --git a/src/sexpots/sexpots.vcxproj b/src/sexpots/sexpots.vcxproj
index b5f846a55c..4370751425 100644
--- a/src/sexpots/sexpots.vcxproj
+++ b/src/sexpots/sexpots.vcxproj
@@ -58,7 +58,7 @@
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <StringPooling>true</StringPooling>
       <FunctionLevelLinking>true</FunctionLevelLinking>
diff --git a/src/smblib/smblib.vcxproj b/src/smblib/smblib.vcxproj
index fa3712691e..bec0680c3d 100644
--- a/src/smblib/smblib.vcxproj
+++ b/src/smblib/smblib.vcxproj
@@ -62,7 +62,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;SMB_EXPORTS;MD5_EXPORTS;B64_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release/smblib.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release/</AssemblerListingLocation>
diff --git a/src/uifc/uifc.vcxproj b/src/uifc/uifc.vcxproj
index 04d73d0139..6b2bf23ead 100644
--- a/src/uifc/uifc.vcxproj
+++ b/src/uifc/uifc.vcxproj
@@ -88,7 +88,7 @@
       <AdditionalIncludeDirectories>..\conio;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release/uifc.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release/</AssemblerListingLocation>
diff --git a/src/xpdev/xpdev.vcxproj b/src/xpdev/xpdev.vcxproj
index 996a194f17..882e3da4bf 100644
--- a/src/xpdev/xpdev.vcxproj
+++ b/src/xpdev/xpdev.vcxproj
@@ -84,7 +84,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;%(PreprocessorDefinitions);HAS_STDINT_H</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release/xpdev.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release/</AssemblerListingLocation>
diff --git a/src/xpdev/xpdev_mt.vcxproj b/src/xpdev/xpdev_mt.vcxproj
index 87f2bf9174..bf2b0141b5 100644
--- a/src/xpdev/xpdev_mt.vcxproj
+++ b/src/xpdev/xpdev_mt.vcxproj
@@ -86,7 +86,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;LINK_LIST_THREADSAFE;%(PreprocessorDefinitions);HAS_STDINT_H</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\msvc.win32.release_mt/xpdev_mt.pch</PrecompiledHeaderOutputFile>
       <AssemblerListingLocation>.\msvc.win32.release_mt/</AssemblerListingLocation>
-- 
GitLab


From 9b0773f326750ebebb57213f05bff1ac989835ed Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 14 Sep 2020 21:45:52 -0700
Subject: [PATCH 692/752] Make the "Source" and "Target" ComboBoxes more
 obvious.

---
 src/sbbs3/ctrl/PropertiesDlgUnit.dfm | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/sbbs3/ctrl/PropertiesDlgUnit.dfm b/src/sbbs3/ctrl/PropertiesDlgUnit.dfm
index e884cbf847..22ff829395 100644
--- a/src/sbbs3/ctrl/PropertiesDlgUnit.dfm
+++ b/src/sbbs3/ctrl/PropertiesDlgUnit.dfm
@@ -326,7 +326,7 @@ object PropertiesDlg: TPropertiesDlg
         Height = 21
         ItemHeight = 13
         TabOrder = 0
-        Text = 'Node List'
+        Text = 'Source'
         OnChange = SourceComboBoxChange
         Items.Strings = (
           'Node List'
@@ -380,6 +380,7 @@ object PropertiesDlg: TPropertiesDlg
         Height = 21
         ItemHeight = 13
         TabOrder = 5
+        Text = 'Target'
         Items.Strings = (
           'Node List'
           'Client List'
-- 
GitLab


From 4efd95e3f1b9906ecc6c9a91876c4bc4d5dcf8d2 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Mon, 14 Sep 2020 22:00:00 -0700
Subject: [PATCH 693/752] Setup mcmlxxix's doors to share data with his BBS.

Hopefully The BRoKEN BUBBLe BBS stays "up". :-/

Also, added Starstocks. Stalk your favorite stars here!
---
 xtrn/bublbogl/install-xtrn.ini      |  8 +++++++-
 xtrn/dicewarz2/install-xtrn.ini     |  8 +++++++-
 xtrn/maze/install-xtrn.ini          | 10 ++++++++--
 xtrn/starstocks/install-xtrn.ini    | 23 +++++++++++++++++++++++
 xtrn/synchronetris/install-xtrn.ini |  8 +++++++-
 xtrn/uberblox/install-xtrn.ini      |  8 +++++++-
 6 files changed, 59 insertions(+), 6 deletions(-)
 create mode 100644 xtrn/starstocks/install-xtrn.ini

diff --git a/xtrn/bublbogl/install-xtrn.ini b/xtrn/bublbogl/install-xtrn.ini
index e045a4a282..9367bea405 100644
--- a/xtrn/bublbogl/install-xtrn.ini
+++ b/xtrn/bublbogl/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: A timed English-word creation game
 By: mcmlxxix (Matt Johnson)
 Cats: Games
 Subs: Puzzle, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 07:26:41 rswindell Exp $
+Inst: 2020/09/14
 
 [prog:BOGGLE]
 cmd  = ?boggle.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with The BRoKEN BUBBLe BBS
+keys = host
+values = 'bbs.thebrokenbubble.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:boggle]
diff --git a/xtrn/dicewarz2/install-xtrn.ini b/xtrn/dicewarz2/install-xtrn.ini
index 4627482b30..0f4415bf9a 100644
--- a/xtrn/dicewarz2/install-xtrn.ini
+++ b/xtrn/dicewarz2/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: Modeled after "Dice Wars" web strategy game involving dice, like Risk
 By: mcmlxxix (Matt Johnson)
 Cats: Games
 Subs: Puzzle, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 09:19:33 rswindell Exp $
+Inst: 2020/09/14
 
 [prog:DICEWAR2]
 cmd  = ?dice2.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with The BRoKEN BUBBLe BBS
+keys = host
+values = 'bbs.thebrokenbubble.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:dicewarz2]
diff --git a/xtrn/maze/install-xtrn.ini b/xtrn/maze/install-xtrn.ini
index 40c313947e..b93ee6972f 100644
--- a/xtrn/maze/install-xtrn.ini
+++ b/xtrn/maze/install-xtrn.ini
@@ -1,15 +1,21 @@
 Name: Maze Race
-Desc: 
+Desc: Race. In a maze.
 By: mcmlxxix (Matt Johnson)
 Cats: Games
 Subs: Race, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/27 02:24:41 rswindell Exp $
+Inst: 2020/09/14
 
 [prog:MAZERACE]
 cmd  = ?maze.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with The BRoKEN BUBBLe BBS
+keys = host
+values = 'bbs.thebrokenbubble.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:mazerace]
diff --git a/xtrn/starstocks/install-xtrn.ini b/xtrn/starstocks/install-xtrn.ini
new file mode 100644
index 0000000000..32e9f001c7
--- /dev/null
+++ b/xtrn/starstocks/install-xtrn.ini
@@ -0,0 +1,23 @@
+Name: Star Stocks
+Desc: A game of galactic investment
+By: mcmlxxix (Matt Johnson)
+Cats: Games
+Subs: Stocks, Multiplayer, JavaScript
+Inst: 2020/09/14
+
+[prog:STARSTOX]
+cmd  = ?stars.js
+settings = XTRN_MULTIUSER
+required = true
+
+[ini:server.ini]
+prompt = Share data with The BRoKEN BUBBLe BBS
+keys = host
+values = 'bbs.thebrokenbubble.com'
+done = true
+
+!include install-json-service.ini
+
+[ini:json-service.ini:starstocks]
+keys=dir
+values=startup_dir
diff --git a/xtrn/synchronetris/install-xtrn.ini b/xtrn/synchronetris/install-xtrn.ini
index a179de1cbd..8f371def8c 100644
--- a/xtrn/synchronetris/install-xtrn.ini
+++ b/xtrn/synchronetris/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: TETRIS
 By:   mcmlxxix (Matt Johnson)
 Cats: Games
 Subs: Puzzle, Clone, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/25 03:12:03 rswindell Exp $
+Inst: 2020/09/14
 
 [prog:TETRIS]
 cmd  = ?tetris.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with The BRoKEN BUBBLe BBS
+keys = host
+values = 'bbs.thebrokenbubble.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:synchronetris]
diff --git a/xtrn/uberblox/install-xtrn.ini b/xtrn/uberblox/install-xtrn.ini
index b31b29a232..d4add3490b 100644
--- a/xtrn/uberblox/install-xtrn.ini
+++ b/xtrn/uberblox/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: Puzzle strategy game similar to that of the popular web game "Super Collap
 By:   mcmlxxix (Matt Johnson) at bbs dot thebrokenbubble dot com
 Cats: Games
 Subs: Puzzle, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:21:38 rswindell Exp $
+Inst: 2020/09/14
 
 [prog:UBERBLOX]
 cmd  = ?blox.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with The BRoKEN BUBBLe BBS
+keys = host
+values = 'bbs.thebrokenbubble.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:uberblox]
-- 
GitLab


From 35785af326a906ecf43519a3815ff7058f4f7f6a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 15 Sep 2020 00:07:53 -0700
Subject: [PATCH 694/752] Fix observed crash on a fresh install (no
 data/user.dat) on Windows XP

Apparently, calling close(-1) on WinXP will cause a crash. Don't do that.
Make closeuserdat() resilient to an un-open file descriptor and use it
instead of close().
---
 src/sbbs3/main.cpp  | 4 ++--
 src/sbbs3/str.cpp   | 2 +-
 src/sbbs3/userdat.c | 2 ++
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index dc20a334cd..f1b26382e5 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -2785,7 +2785,7 @@ void event_thread(void* arg)
 						sbbs->online=FALSE;
 					}
 				}
-				close(userfile);
+				closeuserdat(userfile);
 				lastprepack=(time32_t)now;
 				SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
 				if((file=sbbs->nopen(str,O_WRONLY))==-1) {
@@ -4824,7 +4824,7 @@ void sbbs_t::daily_maint(void)
 			putuserrec(&cfg,user.number,U_MISC,8,ultoa(user.misc|DELETED,str,16));
 		}
 	}
-	close(userfile);
+	closeuserdat(userfile);
 
 	lputs(LOG_INFO,"DAILY: Purging deleted/expired e-mail");
 	SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp
index 3200e3f67f..6607442ebf 100644
--- a/src/sbbs3/str.cpp
+++ b/src/sbbs3/str.cpp
@@ -111,7 +111,7 @@ void sbbs_t::userlist(long mode)
 		}
 		j++; 
 	}
-	close(userfile);
+	closeuserdat(userfile);
 	if(i<=k) {	/* aborted */
 		if(sort)
 			for(i=0;i<j;i++)
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 5b035417cd..c1c7837d6b 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -207,6 +207,8 @@ int openuserdat(scfg_t* cfg, BOOL for_modify)
 
 int closeuserdat(int file)
 {
+	if(file < 1)
+		return -1;
 	return close(file);
 }
 
-- 
GitLab


From dfbf767ea6f6250afc7f8910bd91f1f5dab6250b Mon Sep 17 00:00:00 2001
From: mcmlxxix <mdj1979@gmail.com>
Date: Tue, 15 Sep 2020 13:50:01 -0400
Subject: [PATCH 695/752] Update frame.js

update frame.putmsg() "normal" ctrl-a code to use attributes from frame initialization (ansi "normal" is relative to the frame's settings). to emulate standard "normal" behaviour, initialize frames with BG_BLACK|LIGHTGRAY
---
 exec/load/frame.js | 3520 ++++++++++++++++++++++----------------------
 1 file changed, 1759 insertions(+), 1761 deletions(-)

diff --git a/exec/load/frame.js b/exec/load/frame.js
index bb865f239c..c834fe1f4f 100644
--- a/exec/load/frame.js
+++ b/exec/load/frame.js
@@ -1,1762 +1,1760 @@
-/* $Id: frame.js,v 1.91 2020/08/01 19:32:23 rswindell Exp $ */
-
-/**
- 	Javascript Frame Library
- 	for Synchronet v3.15a+
- 	by Matt Johnson (mcmlxxix)
-
-DESCRIPTION:
-
- 	this library is meant to be used in conjunction with other libraries that
- 	store display data in a Frame() object or objects
- 	this allows for "windows" that can be hidden, moved, closed, etc...
-	without destroying the data behind them.
-
- 	the object itself takes the following parameters:
-
- 		x: 			the coordinate representing the top left corner of the frame (horiz)
- 		y: 			the coordinate representing the top left corner of the frame (vert)
- 		width: 		the horizontal width of the frame
- 		height: 	the vertical height of the frame
- 		attr:		the default color attributes of the frame
-		parent:		a frame object representing the parent of the new frame
-
-METHODS:
-
-	frame.open()				//populate frame contents in character canvas
-	frame.close()				//remove frame contents from character canvas
-	frame.delete()				//delete this frame (remove from screen buffer, destroy internal references)
-	frame.invalidate()			//clear screen buffer to redraw contents on cycle() or draw()
-	frame.draw()				//open frame (if not open) move to top (if not on top) and cycle()
-	frame.cycle()				//check the display matrix for updated characters and displays them
-	frame.refresh()				//flag all frame sectors for potential update
-	frame.load(filename)		//load a binary graphic (.BIN) or ANSI graphic (.ANS) file
-	frame.bottom()				//push frame to bottom of display stack
-	frame.top()					//pull frame to top of display stack
-	frame.scroll(x,y)			//scroll frame n spaces in any direction
-	frame.scrollTo(x,y)			//scroll frame to absolute offset
-	frame.move(x,y)				//move frame n spaces in any direction
-	frame.moveTo(x,y)			//move frame to absolute position
-	frame.end()					//opposite of frame.home()
-	frame.screenShot(file,append)
-								//capture the contents of a frame to file
-	frame.getData(x,y,use_offset)
-								//return the character and attribute located at x,y in frame.data (optional scroll offset)
-	frame.setData(x,y,ch,attr,use_offset)
-								//modify the character and attribute located at x,y in frame.data (optional scroll offset)
-	frame.clearData(x,y,use_offset)
-								//delete the character and attribute located at x,y in frame.data (optional scroll offset)
-	frame.clearline(attr)		//see http://synchro.net/docs/jsobjs.html#console
-	frame.cleartoeol(attr)
-	frame.putmsg(str,attr)
-	frame.clear(attr)
-	frame.home()
-	frame.center(str,attr)
-	frame.crlf()
-	frame.getxy()
-	frame.gotoxy(x,y)
-	frame.pushxy()
-	frame.popxy()
-
-PROPERTIES:
-
-	frame.x						//x screen position
-	frame.y						//y screen position
-	frame.width					//frame width
-	frame.height				//frame height
-	frame.data					//frame data matrix
-	frame.data_height			//true height of frame contents (READ ONLY)
-	frame.data_width			//true width of frame contents (READ ONLY)
-	frame.attr					//default attributes for frame
-	frame.checkbounds			//toggle true/false to restrict/allow frame movement outside display
-	frame.lf_strict				//toggle true/false to force newline after a crlf-terminated string
-	frame.v_scroll				//toggle true/false to enable/disable vertical scrolling
-	frame.h_scroll				//toggle true/false to enable/disable horizontal scrolling
-	frame.scrollbars			//toggle true/false to show/hide scrollbars
-	frame.transparent			//toggle true/false to enable transparency mode
-								//(do not display frame sectors where char == undefined)
-	frame.offset				//current offset object {x,y}
-	frame.cursor				//current cursor object {x,y}
-	frame.parent				//the parent frame of a frame
-	frame.id					//a unique identifier (e.g. "0.1.1.2.3")
-	frame.is_open				//return true is frame is opened in screen buffer
-
-USAGE:
-
-	//create a new frame object at screen position 1,1. 80 characters wide by 24 tall
- 	load("frame.js");
- 	var frame = new Frame(1,1,80,24,BG_BLUE);
-
-	//add frame to the display canvas
-	frame.open();
-
-	//add a new frame within the frame object that will display on top at position 10,10
-	var subframe = new Frame(10,10,10,10,BG_GREEN,frame);
-
-	//add subframe to the display canvas
-	subframe.open();
-
-	//place cursor at position x:5 y:5 relative to subframe's coordinates
-	subframe.gotoxy(5,5);
-
-	//beware this sample infinite loop
- 	while(!js.terminated) {
-		//print a message into subframe
-		subframe.putmsg("1");
-
-		//on first call this will draw the entire initial frame,
-		//as triggered by the open() method call.
-		//on subsequent calls this will draw only areas that have changed
-		frame.cycle();
-		//NOTE: if frames are linked, only one frame needs to be cycled
-		//		for all frames to update
-	}
-
-	//close out the entire frame tree
-	frame.close();
-
- */
-
-load("sbbsdefs.js");
-function Frame(x,y,width,height,attr,parent) {
-
-	/* private properties */
-	this.__properties__ = {
-		x:undefined,
-		y:undefined,
-		width:undefined,
-		height:undefined,
-		attr:undefined,
-		display:undefined,
-		data:[],
-		open:false,
-		ctrl_a:false,
-		curr_attr:undefined,
-    attr_stack:[],
-		id:0
-	};
-	this.__settings__ = {
-		v_scroll:true,
-		h_scroll:false,
-		scrollbars:false,
-		lf_strict:true,
-		checkbounds:true,
-		transparent:false,
-		word_wrap:false
-	};
-	this.__relations__ = {
-		parent:undefined,
-		child:[]
-	};
-	this.__position__ = {
-		cursor:undefined,
-		offset:undefined,
-		stored:undefined
-	};
-
-	function init(x,y,width,height,attr,parent) {
-		if(parent instanceof Frame) {
-			this.__properties__.id = parent.display.nextID;
-			this.__properties__.display = parent.display;
-			this.__settings__.checkbounds = parent.checkbounds;
-			this.__relations__.parent = parent;
-			parent.child = this;
-		}
-		else {
-			this.__properties__.display = new Display(x,y,width,height);
-		}
-
-		this.x = x;
-		this.y = y;
-		this.width = width;
-		this.height = height;
-		this.attr = attr;
-
-		this.__position__.cursor = new Cursor(0,0,this);
-		this.__position__.offset = new Offset(0,0,this);
-		this.__position__.stored = new Cursor(0,0,this);
-
-		//log(LOG_DEBUG,format("new frame initialized: %sx%s at %s,%s",this.width,this.height,this.x,this.y));
-	}
-	init.apply(this,arguments);
-}
-
-/* protected properties */
-Frame.prototype.__defineGetter__("child", function() {
-	return this.__relations__.child;
-});
-Frame.prototype.__defineSetter__("child", function(frame) {
-	if(frame instanceof Frame)
-		this.__relations__.child.push(frame);
-	else
-		throw("child not an instance of Frame()");
-});
-Frame.prototype.__defineGetter__("attr", function() {
-	return this.__properties__.attr;
-});
-Frame.prototype.__defineSetter__("attr", function(attr) {
-	if(attr !== undefined && isNaN(attr))
-		throw("invalid attribute: " + attr);
-	this.__properties__.attr = attr;
-});
-Frame.prototype.__defineGetter__("x", function() {
-	if(this.__properties__.x == undefined)
-		return this.__properties__.display.x;
-	return this.__properties__.x;
-});
-Frame.prototype.__defineSetter__("x", function(x) {
-	if(x == undefined)
-		return;
-	if(!this.__checkX__(x))
-		throw("invalid x coordinate: " + x);
-	this.__properties__.x = Number(x);
-});
-Frame.prototype.__defineGetter__("y", function() {
-	if(this.__properties__.y == undefined)
-		return this.__properties__.display.y;
-	return this.__properties__.y;
-});
-Frame.prototype.__defineSetter__("y", function(y) {
-	if(y == undefined)
-		return;
-	if(!this.__checkY__(y))
-		throw("invalid y coordinate: " + y);
-	this.__properties__.y = Number(y);
-});
-Frame.prototype.__defineGetter__("width", function() {
-	if(this.__properties__.width == undefined)
-		return this.__properties__.display.width;
-	return this.__properties__.width;
-});
-Frame.prototype.__defineSetter__("width", function(width) {
-	if(width == undefined)
-		return;
-	if(!this.__checkWidth__(this.x,Number(width)))
-		throw("invalid width: " + width);
-	this.__properties__.width = Number(width);
-});
-Frame.prototype.__defineGetter__("height", function() {
-	if(this.__properties__.height == undefined)
-		return this.__properties__.display.height;
-	return this.__properties__.height;
-});
-Frame.prototype.__defineSetter__("height", function(height) {
-	if(height == undefined)
-		return;
-	if(!this.__checkHeight__(this.y,Number(height)))
-		throw("invalid height: " + height);
-	this.__properties__.height = Number(height);
-});
-
-/* read-only properties */
-Frame.prototype.__defineGetter__("cursor",function() {
-	return this.__position__.cursor;
-});
-Frame.prototype.__defineGetter__("offset",function() {
-	return this.__position__.offset;
-});
-Frame.prototype.__defineGetter__("id", function() {
-	return this.__properties__.id;
-});
-Frame.prototype.__defineGetter__("parent", function() {
-	return this.__relations__.parent;
-});
-Frame.prototype.__defineGetter__("display", function() {
-	return this.__properties__.display;
-});
-Frame.prototype.__defineGetter__("data_height", function() {
-	return this.__properties__.data.length;
-});
-Frame.prototype.__defineGetter__("data_width", function() {
-	if(typeof this.__properties__.data[0] == "undefined")
-		return 0;
-	var longest = 0;
-	for(var d in this.__properties__.data) {
-		if(this.__properties__.data[d].length > longest)
-			longest = this.__properties__.data[d].length;
-	}
-	return longest + 1;
-});
-Frame.prototype.__defineGetter__("data", function() {
-	return this.__properties__.data;
-});
-
-/* protected settings */
-Frame.prototype.__defineGetter__("checkbounds", function() {
-	return this.__settings__.checkbounds;
-});
-Frame.prototype.__defineSetter__("checkbounds", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.checkbounds=bool;
-	else
-		throw("non-boolean checkbounds: " + bool);
-});
-Frame.prototype.__defineGetter__("transparent", function() {
-	return this.__settings__.transparent;
-});
-Frame.prototype.__defineSetter__("transparent", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.transparent=bool;
-	else
-		throw("non-boolean transparent: " + bool);
-});
-Frame.prototype.__defineGetter__("lf_strict", function() {
-	return this.__settings__.lf_strict;
-});
-Frame.prototype.__defineSetter__("lf_strict", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.lf_strict=bool;
-	else
-		throw("non-boolean lf_strict: " + bool);
-});
-Frame.prototype.__defineGetter__("scrollbars", function() {
-	return this.__settings__.scrollbars;
-});
-Frame.prototype.__defineSetter__("scrollbars", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.scrollbars=bool;
-	else
-		throw("non-boolean scrollbars: " + bool);
-});
-Frame.prototype.__defineGetter__("v_scroll", function() {
-	return this.__settings__.v_scroll;
-});
-Frame.prototype.__defineSetter__("v_scroll", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.v_scroll=bool;
-	else
-		throw("non-boolean v_scroll: " + bool);
-});
-Frame.prototype.__defineGetter__("word_wrap", function() {
-	return this.__settings__.word_wrap;
-});
-Frame.prototype.__defineSetter__("word_wrap", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.word_wrap=bool;
-	else
-		throw("non-boolean word_wrap: " + bool);
-});
-Frame.prototype.__defineGetter__("h_scroll", function() {
-	return this.__settings__.h_scroll;
-});
-Frame.prototype.__defineSetter__("h_scroll", function(bool) {
-	if(typeof bool == "boolean")
-		this.__settings__.h_scroll=bool;
-	else
-		throw("non-boolean h_scroll: " + bool);
-});
-Frame.prototype.__defineGetter__("is_open",function() {
-	return this.__properties__.open;
-});
-
-/* public methods */
-Frame.prototype.getData = function(x,y,use_offset) {
-	var px = x;
-	var py = y;
-	if(use_offset) {
-		px += this.__position__.offset.x;
-		py += this.__position__.offset.y;
-	}
-	// if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
-		// throw("Frame.getData() - invalid coordinates: " + px + "," + py);
-	if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
-		return new Char();
-	return this.__properties__.data[py][px];
-}
-Frame.prototype.setData = function(x,y,ch,attr,use_offset) {
-	var px = x;
-	var py = y;
-	if(use_offset) {
-		px += this.__position__.offset.x;
-		py += this.__position__.offset.y;
-	}
-	//I don't remember why I did this, but it was probably important at the time
-	//if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
-		// throw("Frame.setData() - invalid coordinates: " + px + "," + py);
-	if(!this.__properties__.data[py])
-		this.__properties__.data[py] = [];
-	if(!this.__properties__.data[py][px])
-		this.__properties__.data[py][px] = new Char();
-	if(this.__properties__.data[py][px].ch == ch && this.__properties__.data[py][px].attr == attr)
-		return;
-	if(ch)
-		this.__properties__.data[py][px].ch = ch;
-	if(attr)
-		this.__properties__.data[py][px].attr = attr;
-	if(this.__properties__.open)
-		this.__properties__.display.updateChar(this,x,y);
-}
-Frame.prototype.getWord = function(x,y) {
-	var word = []
-	var nx = x-this.x;
-	var ny = y-this.y;
-	var cell = this.getData(nx,ny,false);
-	while(nx >= 0 && cell != undefined && cell.ch != undefined && cell.ch.match(/[0-9a-zA-Z]/)) {
-		word.unshift(cell.ch);
-		nx--;
-		cell = this.getData(nx,ny,false);
-	}
-	nx = x-this.x+1;
-	cell = this.getData(nx,ny,false);
-	while(nx < this.width && cell != undefined && cell.ch != undefined && cell.ch.match(/[0-9a-zA-Z]/)) {
-		word.push(cell.ch);
-		nx++;
-		cell = this.getData(nx,ny,false);
-	}
-	return word.join("");
-}
-Frame.prototype.clearData = function(x,y,use_offset) {
-	var px = x;
-	var py = y;
-	if(use_offset) {
-		px += this.__position__.offset.x;
-		py += this.__position__.offset.y;
-	}
-	if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
-		return;
-	else if(this.__properties__.data[py][px].ch == undefined && this.__properties__.data[py][px].attr == undefined)
-		return;
-	this.__properties__.data[py][px].ch = undefined;
-	this.__properties__.data[py][px].attr = undefined;
-	if(this.__properties__.open)
-		this.__properties__.display.updateChar(this,x,y);
-}
-Frame.prototype.bottom = function() {
-	if(this.__properties__.open) {
-		for each(var c in this.__relations__.child)
-			c.bottom();
-		this.__properties__.display.bottom(this);
-	}
-}
-Frame.prototype.top = function() {
-	if(this.__properties__.open) {
-		this.__properties__.display.top(this);
-		for each(var c in this.__relations__.child)
-			c.top();
-	}
-}
-Frame.prototype.open = function() {
-	if(!this.__properties__.open) {
-		this.__properties__.display.open(this);
-		this.__properties__.open = true;
-	}
-	for each(var c in this.__relations__.child) {
-		c.open();
-	}
-}
-Frame.prototype.refresh = function() {
-	if(this.__properties__.open) {
-		this.__properties__.display.updateFrame(this);
-		for each(var c in this.__relations__.child)
-			c.refresh();
-	}
-}
-Frame.prototype.close = function() {
-	for each(var c in this.__relations__.child)
-		c.close();
-	if(this.__properties__.open) {
-		this.__properties__.display.close(this);
-		this.__properties__.open = false;
-	}
-}
-Frame.prototype.delete = function(id) {
-	if(id == undefined) {
-		this.close();
-		if(this.__relations__.parent) {
-			this.__relations__.parent.delete(this.id);
-		}
-	}
-	else {
-		for(var c=0;c<this.__relations__.child.length;c++) {
-			if(this.__relations__.child[c].id == id) {
-				this.__relations__.child.splice(c--,1);
-				break;
-			}
-		}
-	}
-}
-Frame.prototype.move = function(x,y) {
-	var nx = undefined;
-	var ny = undefined;
-	if(this.__checkX__(this.x+x) && this.__checkWidth__(this.x+x,this.width))
-		nx = this.x+x;
-	if(this.__checkY__(this.y+y) && this.__checkHeight__(this.y+y,this.height))
-		ny = this.y+y;
-	if(nx == undefined && ny == undefined)
-		return;
-	this.__properties__.display.updateFrame(this);
-	if(nx !== undefined)
-		this.x=nx;
-	if(ny !== undefined)
-		this.y=ny;
-	this.__properties__.display.updateFrame(this);
-	for each(var c in this.__relations__.child)
-		c.move(x,y);
-}
-Frame.prototype.moveTo = function(x,y) {
-	for each(var c in this.__relations__.child) {
-		var cx = (x + (c.x-this.x));
-		var cy = (y + (c.y-this.y));
-		c.moveTo(cx,cy);
-	}
-	var nx = undefined;
-	var ny = undefined;
-	if(this.__checkX__(x))
-		nx = x;
-	if(this.__checkY__(y))
-		ny = y;
-	if(nx == undefined && ny == undefined)
-		return;
-	this.__properties__.display.updateFrame(this);
-	if(nx !== undefined)
-		this.x=nx;
-	if(ny !== undefined)
-		this.y=ny;
-	this.__properties__.display.updateFrame(this);
-}
-Frame.prototype.draw = function() {
-	if(this.__properties__.open)
-		this.refresh();
-	else
-		this.open();
-	this.cycle();
-}
-Frame.prototype.cycle = function() {
-	return this.__properties__.display.cycle();
-}
-Frame.prototype.load = function(filename,width,height) {
-	var f=new File(filename);
-	if (!f.open("rb", true))
-		return false;
-	var contents=f.read();
-	f.close();
-	var valid_sauce = false;
-	var ext = file_getext(filename).substr(1).toUpperCase();
-
-	if (contents.substr(-128, 7) == "SAUCE00") {
-		var sauceless_size = ascii(contents.substr(-35,1));
-		sauceless_size <<= 8;
-		sauceless_size |= ascii(contents.substr(-36,1));
-		sauceless_size <<= 8;
-		sauceless_size |= ascii(contents.substr(-37,1));
-		sauceless_size <<= 8;
-		sauceless_size |= ascii(contents.substr(-38,1));
-
-		var data_type = ascii(contents.substr(-34,1));
-		var file_type = ascii(contents.substr(-33,1));
-		var tinfo1 = ascii(contents.substr(-31,1));
-		tinfo1 <<= 8;
-		tinfo1 |= ascii(contents.substr(-32,1));
-		var tinfo2 = ascii(contents.substr(-29,1));
-		tinfo2 <<= 8;
-		tinfo2 |= ascii(contents.substr(-30,1));
-		switch(data_type) {
-			case 1:
-				switch(file_type) {
-					case 0:	// PLain ASCII
-						ext = "TXT";
-						if (tinfo1)
-							width = tinfo1;
-						if (tinfo2)
-							height = tinfo2;
-						break;
-					case 1: // ANSi
-						ext = "ANS";
-						if (tinfo1)
-							width = tinfo1;
-						if (tinfo2)
-							height = tinfo2;
-						break;
-					case 7: // Source
-						ext = "TXT";
-						break;
-				}
-				valid_sauce = true;
-				break;
-			case 5:
-				ext = 'BIN';
-				width = file_type * 2;
-				height = (sauceless_size / 2) / width;
-				valid_sauce = true;
-				break;
-		}
-		if (valid_sauce)
-			contents = contents.substr(0, sauceless_size);
-	}
-
-	switch(ext) {
-	case "ANS":
-		/*
-		 * TODO: This doesn't do exactly what reading a text file does
-		 * one Windows (nor on Linux), but it should be close to what
-		 * was meant, and should work given that it resets x every line.
-		 */
-		var lines=contents.split(/\r\n/);
-
-		var attr = this.attr;
-		var bg = BG_BLACK;
-		var fg = LIGHTGRAY;
-
-		var i = 0;
-		var y = 0;
-		var saved = {};
-
-		while(lines.length > 0) {
-			var x = 0;
-			var line = lines.shift();
-			/* parse 'ATCODES'??
-			line = line.replace(/@(.*)@/g,
-				function (str, code, offset, s) {
-					return bbs.atcode(code);
-				}
-			);
-			*/
-			while(line.length > 0) {
-				/* parse an attribute sequence*/
-				var m = line.match(/^\x1b\[(\d*);?(\d*);?(\d*)m/);
-				if(m !== null) {
-					line = line.substr(m.shift().length);
-					if(m[0] == 0) {
-						bg = BG_BLACK;
-						fg = LIGHTGRAY;
-						i = 0;
-						m.shift();
-					}
-					if(m[0] == 1) {
-						i = HIGH;
-						m.shift();
-					}
-					if(m[0] >= 40) {
-						switch(Number(m.shift())) {
-						case 40:
-							bg = BG_BLACK;
-							break;
-						case 41:
-							bg = BG_RED;
-							break;
-						case 42:
-							bg = BG_GREEN;
-							break;
-						case 43:
-							bg = BG_BROWN;
-							break;
-						case 44:
-							bg = BG_BLUE;
-							break;
-						case 45:
-							bg = BG_MAGENTA;
-							break;
-						case 46:
-							bg = BG_CYAN;
-							break;
-						case 47:
-							bg = BG_LIGHTGRAY;
-							break;
-						}
-					}
-					if(m[0] >= 30) {
-						switch(Number(m.shift())) {
-						case 30:
-							fg = BLACK;
-							break;
-						case 31:
-							fg = RED;
-							break;
-						case 32:
-							fg = GREEN;
-							break;
-						case 33:
-							fg = BROWN;
-							break;
-						case 34:
-							fg = BLUE;
-							break;
-						case 35:
-							fg = MAGENTA;
-							break;
-						case 36:
-							fg = CYAN;
-							break;
-						case 37:
-							fg = LIGHTGRAY;
-							break;
-						}
-					}
-					attr = bg + fg + i;
-					continue;
-				}
-
-				/* parse absolute character position */
-				var m = line.match(/^\x1b\[(\d*);?(\d*)[Hf]/);
-				if(m !== null) {
-					line = line.substr(m.shift().length);
-
-					if(m.length==0) {
-						x=0;
-						y=0;
-					}
-					else {
-						if(m[0])
-							y = Number(m.shift())-1;
-						if(m[0])
-							x = Number(m.shift())-1;
-					}
-					continue;
-				}
-
-				/* ignore a bullshit sequence */
-				var n = line.match(/^\x1b\[\?7h/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					continue;
-				}
-
-				/* parse an up positional sequence */
-				var n = line.match(/^\x1b\[(\d*)A/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					var chars = n.shift();
-					if(chars < 1)
-						y-=1;
-					else
-						y-=Number(chars);
-					continue;
-				}
-
-				/* parse a down positional sequence */
-				var n = line.match(/^\x1b\[(\d*)B/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					var chars = n.shift();
-					if(chars < 1)
-						y+=1;
-					else
-						y+=Number(chars);
-					continue;
-				}
-
-				/* parse a forward positional sequence */
-				var n = line.match(/^\x1b\[(\d*)C/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					var chars = n.shift();
-					if(chars < 1)
-						x+=1;
-					else
-						x+=Number(chars);
-					continue;
-				}
-
-				/* parse a backward positional sequence */
-				var n = line.match(/^\x1b\[(\d*)D/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					var chars = n.shift()
-					if(chars < 1)
-						x-=1;
-					else
-						x-=Number(chars);
-					continue;
-				}
-
-				/* parse a clear screen sequence */
-				var n = line.match(/^\x1b\[2J/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					continue;
-				}
-
-				/* parse save cursor sequence */
-				var n = line.match(/^\x1b\[s/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					saved.x = x;
-					saved.y = y;
-					continue;
-				}
-
-				/* parse restore cursor sequence */
-				var n = line.match(/^\x1b\[u/);
-				if(n !== null) {
-					line = line.substr(n.shift().length);
-					x = saved.x;
-					y = saved.y;
-					continue;
-				}
-
-				/* set character and attribute */
-				var ch = line[0];
-				line = line.substr(1);
-
-				/* validate position */
-				if(y<0)
-					y=0;
-				if(x<0)
-					x=0;
-				if(x>=this.width) {
-					x=0;
-					y++;
-				}
-				/* set character and attribute */
-				if(!this.__properties__.data[y])
-					this.__properties__.data[y]=[];
-				this.__properties__.data[y][x]=new Char(ch,attr);
-				x++;
-			}
-			y++;
-		}
-		break;
-    case "BIN":
-        if (!this.load_bin(contents, width, height, 0)) return false;
-		break;
-	case "TXT":
-		var lines=contents.split(/\r\n/);
-		while(lines.length > 0)
-			this.putmsg(lines.shift() + "\r\n");
-		break;
-	default:
-		throw("unsupported filetype");
-		break;
-	}
-}
-Frame.prototype.load_bin = function(contents, width, height, offset) {
-    if(width == undefined || height == undefined)
-        throw("unknown graphic dimensions");
-    if(offset == undefined) offset = 0;
-    for(var y=0; y<height; y++) {
-        for(var x=0; x<width; x++) {
-            var c = new Char();
-            if(offset >= contents.length)
-                return(false);
-            c.ch = contents.substr(offset++, 1);
-            if(offset == contents.length)
-                return(false);
-            c.attr = ascii(contents.substr(offset++, 1));
-            c.id = this.id;
-            if(!this.__properties__.data[y])
-                this.__properties__.data[y]=[];
-            this.__properties__.data[y][x] = c;
-        }
-    }
-    return true;
-}
-Frame.prototype.scroll = function(x,y) {
-	var update = false;
-	/* default: add a new line to the data matrix */
-	if(x == undefined && y == undefined) {
-		if(this.__settings__.v_scroll) {
-			var newrow = [];
-			for(var x = 0;x<this.width;x++) {
-				for(var y = 0;y<this.height;y++)
-					this.__properties__.display.updateChar(this,x,y);
-				newrow.push(new Char());
-			}
-			this.__properties__.data.push(newrow);
-			this.__position__.offset.y++;
-			update = true;
-		}
-	}
-	/* otherwise, adjust the x/y offset */
-	else {
-		if(typeof x == "number" && x !== 0 && this.__settings__.h_scroll) {
-			this.__position__.offset.x += x;
-			update = true;
-		}
-		if(typeof y == "number" && y !== 0 && this.__settings__.v_scroll) {
-			this.__position__.offset.y += y;
-			update = true;
-		}
-		if(update)
-			this.refresh();
-	}
-	return update;
-}
-Frame.prototype.scrollTo = function(x,y) {
-	var update = false;
-	if(typeof x == "number") {
-		if(this.__settings__.h_scroll) {
-			this.__position__.offset.x = x;
-			update = true;
-		}
-	}
-	if(typeof y == "number") {
-		if(this.__settings__.v_scroll) {
-			this.__position__.offset.y = y;
-			update = true;
-		}
-	}
-	if(update)
-		this.refresh();
-}
-Frame.prototype.insertLine = function(y) {
-	if(this.__properties__.data[y])
-		this.__properties__.data.splice(y,0,[]);
-	else
-		this.__properties__.data[y] = [];
-	this.refresh();
-	return this.__properties__.data[y];
-}
-Frame.prototype.deleteLine = function(y) {
-	var l = undefined;
-	if(this.__properties__.data[y]) {
-		l = this.__properties__.data.splice(y,1);
-		this.refresh();
-	}
-	return l;
-}
-Frame.prototype.screenShot = function(file,append) {
-	return this.__properties__.display.screenShot(file,append);
-}
-Frame.prototype.invalidate = function() {
-	this.__properties__.display.invalidate();
-	this.refresh();
-}
-Frame.prototype.dump = function() {
-	return this.__properties__.display.dump();
-}
-
-/* console method emulation */
-Frame.prototype.home = function() {
-	this.__position__.cursor.x = 0;
-	this.__position__.cursor.y = 0;
-	return true;
-}
-Frame.prototype.end = function() {
-	this.__position__.cursor.x = this.width-1;
-	this.__position__.cursor.y = this.height-1;
-	return true;
-}
-Frame.prototype.pagedown = function() {
-	this.__position__.offset.y += this.height-1;
-	if(this.__position__.offset.y >= this.data_height)
-		this.__position__.offset.y = this.data_height - this.height;
-	this.refresh();
-}
-Frame.prototype.pageup = function() {
-	this.__position__.offset.y -= this.height-1;
-	if(this.__position__.offset.y < 0)
-		this.__position__.offset.y = 0;
-	this.refresh();
-}
-Frame.prototype.clear = function (attr) {
-	if (attr) this.attr = attr;
-	this.__properties__.data = [];
-	this.__position__.offset.x = 0;
-	this.__position__.offset.y = 0;
-	this.home();
-	this.invalidate();
-}
-Frame.prototype.erase = function(ch, attr) {
-	if(attr == undefined)
-		attr = this.attr;
-	var px = this.__position__.offset.x;
-	var py = this.__position__.offset.y;
-	for(var y = 0; y< this.height; y++) {
-		if(!this.__properties__.data[py + y]) {
-			continue;
-		}
-		for(var x = 0; x<this.width; x++) {
-			if(!this.__properties__.data[py + y][px + x]) {
-				continue;
-			}
-			if((this.__properties__.data[py + y][px + x].ch === undefined || 
-				this.__properties__.data[py + y][px + x].ch === ch) && 
-				this.__properties__.data[py + y][px + x].attr == attr) {
-				continue;
-			}
-			this.__properties__.data[py + y][px + x].ch = undefined;
-			this.__properties__.data[py + y][px + x].attr = attr;
-			this.__properties__.display.updateChar(this, x, y);
-		}
-	}
-	this.home();
-}
-Frame.prototype.clearline = function(attr) {
-	if(attr == undefined)
-		attr = this.attr;
-	var py = this.__position__.offset.y + this.__position__.cursor.y;
-	if(!this.__properties__.data[py])
-		return false;
-	for(var x=0;x<this.__properties__.data[py].length;x++) {
-		if(this.__properties__.data[py][x]) {
-			this.__properties__.data[py][x].ch = undefined;
-			this.__properties__.data[py][x].attr = attr;
-		}
-	}
-	for(var x=0;x<this.width;x++) {
-		this.__properties__.display.updateChar(this,x,this.__position__.cursor.y);
-	}
-}
-Frame.prototype.cleartoeol = function(attr) {
-	if(attr == undefined)
-		attr = this.attr;
-	var px = this.__position__.offset.x + this.__position__.cursor.x;
-	var py = this.__position__.offset.y + this.__position__.cursor.y;
-	if(!this.__properties__.data[this.__position__.cursor.y])
-		return false;
-	for(var x=px;x<this.__properties__.data[py].length;x++) {
-		if(this.__properties__.data[py][x]) {
-			if(this.__properties__.data[py][x].ch !== undefined) {
-				this.__properties__.data[py][x].ch = undefined;
-				this.__properties__.display.updateChar(this,x - this.__position__.offset.x, this.__position__.cursor.y);
-			}
-			this.__properties__.data[py][x].attr = attr;
-		}
-	}
-}
-Frame.prototype.crlf = function() {
-	this.__position__.cursor.x = 0;
-	if(this.__position__.cursor.y < this.height-1)
-		this.__position__.cursor.y += 1;
-	else {}
-}
-Frame.prototype.write = function(str,attr) {
-	if(str == undefined)
-		return;
-	if(this.__settings__.word_wrap)
-		str = word_wrap(str, this.width,str.length, false);
-	str = str.toString().split('');
-
-	if(attr)
-		this.__properties__.curr_attr = attr;
-	else
-		this.__properties__.curr_attr = this.attr;
-	var pos = this.__position__.cursor;
-	while(str.length > 0) {
-		var ch = str.shift();
-		this.__putChar__.call(this,ch,this.__properties__.curr_attr);
-		pos.x++;
-	}
-}
-Frame.prototype.putmsg = function(str,attr) {
-	if(str == undefined)
-		return;
-	if(this.__settings__.word_wrap) {
-		var remainingWidth = this.width - this.__position__.cursor.x;
-		if(str.length > remainingWidth) {
-			str = word_wrap(str,remainingWidth,str.length,false).split('\n');
-			var trailing_newline = str[str.length - 1].length == 0;
-			str = str.shift() + '\r\n' + word_wrap(str.join('\r\n'),this.width,remainingWidth,false);
-			if(!trailing_newline) str = str.trimRight();
-		}
-	}
-	str = str.toString().split('');
-
-	if(attr)
-		this.__properties__.curr_attr = attr;
-	else
-		this.__properties__.curr_attr = this.attr;
-	var pos = this.__position__.cursor;
-
-	while(str.length > 0) {
-		var ch = str.shift();
-		if(this.__properties__.ctrl_a) {
-			var k = ch;
-			if(k)
-				k = k.toUpperCase();
-			switch(k) {
-			case '\1':	/* A "real" ^A code */
-				this.__putChar__.call(this,ch,this.__properties__.curr_attr);
-				pos.x++;
-				break;
-			case 'K':	/* Black */
-				this.__properties__.curr_attr=(this.__properties__.curr_attr)&0xf8;
-				break;
-			case 'R':	/* Red */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|RED;
-				break;
-			case 'G':	/* Green */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|GREEN;
-				break;
-			case 'Y':	/* Yellow */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|BROWN;
-				break;
-			case 'B':	/* Blue */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|BLUE;
-				break;
-			case 'M':	/* Magenta */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|MAGENTA;
-				break;
-			case 'C':	/* Cyan */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|CYAN;
-				break;
-			case 'W':	/* White */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|LIGHTGRAY;
-				break;
-			case '0':	/* Black */
-				this.__properties__.curr_attr=(this.__properties__.curr_attr)&0x8f;
-				break;
-			case '1':	/* Red */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(RED<<4);
-				break;
-			case '2':	/* Green */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(GREEN<<4);
-				break;
-			case '3':	/* Yellow */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(BROWN<<4);
-				break;
-			case '4':	/* Blue */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(BLUE<<4);
-				break;
-			case '5':	/* Magenta */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(MAGENTA<<4);
-				break;
-			case '6':	/* Cyan */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(CYAN<<4);
-				break;
-			case '7':	/* White */
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(LIGHTGRAY<<4);
-				break;
-			case 'H':	/* High Intensity */
-				this.__properties__.curr_attr|=HIGH;
-				break;
-			case 'I':	/* Blink */
-				this.__properties__.curr_attr|=BLINK;
-				break;
-			case 'N': 	/* Normal */
-				this.__properties__.curr_attr&=~HIGH;
-				this.__properties__.curr_attr&=~BLINK;
-				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|LIGHTGRAY;
-				break;
-      case '+':
-        this.__properties__.attr_stack.push(this.__properties__.curr_attr);
-        break;
-			case '-':	/* Normal if High, Blink, or BG */
-        if (this.__properties__.attr_stack.length) {
-          this.__properties__.curr_attr = this.__properties__.attr_stack.pop();
-        } else if(this.__properties__.curr_attr & 0xf8) {
-					this.__properties__.curr_attr=this.attr;
-        }
-				break;
-			case '_':	/* Normal if blink/background */
-				if(this.__properties__.curr_attr & 0xf0)
-					this.__properties__.curr_attr=this.attr;
-				break;
-			case '[':	/* CR */
-				pos.x=0;
-				break;
-			case ']':	/* LF */
-				pos.y++;
-				if(this.__settings__.lf_strict && pos.y >= this.height) {
-					this.scroll();
-					pos.y--;
-				}
-				break;
-			default:	/* Other stuff... specifically, check for right movement */
-				if(ch.charCodeAt(0)>127)
-					pos.x+=ch.charCodeAt(0)-127;
-				break;
-			}
-			this.__properties__.ctrl_a = false;
-		}
-		else {
-			switch(ch) {
-			case '\1':		/* CTRL-A code */
-			case ctrl('A'):
-				this.__properties__.ctrl_a = true;
-				break;
-			case '\7':		/* Beep */
-				break;
-			case '\x7f':	/* DELETE */
-				break;
-			case '\b':
-				if(pos.x > 0) {
-					pos.x--;
-					this.__putChar__.call(this," ",this.__properties__.curr_attr);
-				}
-				break;
-			case '\r':
-				pos.x=0;
-				break;
-			case '\t':
-				pos.x += 4;
-				break;
-			case '\n':
-				pos.y++;
-				if(this.__settings__.lf_strict && pos.y >= this.height) {
-					this.scroll();
-					pos.y--;
-				}
-				break;
-			default:
-				this.__putChar__.call(this,ch,this.__properties__.curr_attr);
-				pos.x++;
-				break;
+/* $Id: frame.js,v 1.91 2020/08/01 19:32:23 rswindell Exp $ */
+
+/**
+ 	Javascript Frame Library
+ 	for Synchronet v3.15a+
+ 	by Matt Johnson (mcmlxxix)
+
+DESCRIPTION:
+
+ 	this library is meant to be used in conjunction with other libraries that
+ 	store display data in a Frame() object or objects
+ 	this allows for "windows" that can be hidden, moved, closed, etc...
+	without destroying the data behind them.
+
+ 	the object itself takes the following parameters:
+
+ 		x: 			the coordinate representing the top left corner of the frame (horiz)
+ 		y: 			the coordinate representing the top left corner of the frame (vert)
+ 		width: 		the horizontal width of the frame
+ 		height: 	the vertical height of the frame
+ 		attr:		the default color attributes of the frame
+		parent:		a frame object representing the parent of the new frame
+
+METHODS:
+
+	frame.open()				//populate frame contents in character canvas
+	frame.close()				//remove frame contents from character canvas
+	frame.delete()				//delete this frame (remove from screen buffer, destroy internal references)
+	frame.invalidate()			//clear screen buffer to redraw contents on cycle() or draw()
+	frame.draw()				//open frame (if not open) move to top (if not on top) and cycle()
+	frame.cycle()				//check the display matrix for updated characters and displays them
+	frame.refresh()				//flag all frame sectors for potential update
+	frame.load(filename)		//load a binary graphic (.BIN) or ANSI graphic (.ANS) file
+	frame.bottom()				//push frame to bottom of display stack
+	frame.top()					//pull frame to top of display stack
+	frame.scroll(x,y)			//scroll frame n spaces in any direction
+	frame.scrollTo(x,y)			//scroll frame to absolute offset
+	frame.move(x,y)				//move frame n spaces in any direction
+	frame.moveTo(x,y)			//move frame to absolute position
+	frame.end()					//opposite of frame.home()
+	frame.screenShot(file,append)
+								//capture the contents of a frame to file
+	frame.getData(x,y,use_offset)
+								//return the character and attribute located at x,y in frame.data (optional scroll offset)
+	frame.setData(x,y,ch,attr,use_offset)
+								//modify the character and attribute located at x,y in frame.data (optional scroll offset)
+	frame.clearData(x,y,use_offset)
+								//delete the character and attribute located at x,y in frame.data (optional scroll offset)
+	frame.clearline(attr)		//see http://synchro.net/docs/jsobjs.html#console
+	frame.cleartoeol(attr)
+	frame.putmsg(str,attr)
+	frame.clear(attr)
+	frame.home()
+	frame.center(str,attr)
+	frame.crlf()
+	frame.getxy()
+	frame.gotoxy(x,y)
+	frame.pushxy()
+	frame.popxy()
+
+PROPERTIES:
+
+	frame.x						//x screen position
+	frame.y						//y screen position
+	frame.width					//frame width
+	frame.height				//frame height
+	frame.data					//frame data matrix
+	frame.data_height			//true height of frame contents (READ ONLY)
+	frame.data_width			//true width of frame contents (READ ONLY)
+	frame.attr					//default attributes for frame
+	frame.checkbounds			//toggle true/false to restrict/allow frame movement outside display
+	frame.lf_strict				//toggle true/false to force newline after a crlf-terminated string
+	frame.v_scroll				//toggle true/false to enable/disable vertical scrolling
+	frame.h_scroll				//toggle true/false to enable/disable horizontal scrolling
+	frame.scrollbars			//toggle true/false to show/hide scrollbars
+	frame.transparent			//toggle true/false to enable transparency mode
+								//(do not display frame sectors where char == undefined)
+	frame.offset				//current offset object {x,y}
+	frame.cursor				//current cursor object {x,y}
+	frame.parent				//the parent frame of a frame
+	frame.id					//a unique identifier (e.g. "0.1.1.2.3")
+	frame.is_open				//return true is frame is opened in screen buffer
+
+USAGE:
+
+	//create a new frame object at screen position 1,1. 80 characters wide by 24 tall
+ 	load("frame.js");
+ 	var frame = new Frame(1,1,80,24,BG_BLUE);
+
+	//add frame to the display canvas
+	frame.open();
+
+	//add a new frame within the frame object that will display on top at position 10,10
+	var subframe = new Frame(10,10,10,10,BG_GREEN,frame);
+
+	//add subframe to the display canvas
+	subframe.open();
+
+	//place cursor at position x:5 y:5 relative to subframe's coordinates
+	subframe.gotoxy(5,5);
+
+	//beware this sample infinite loop
+ 	while(!js.terminated) {
+		//print a message into subframe
+		subframe.putmsg("1");
+
+		//on first call this will draw the entire initial frame,
+		//as triggered by the open() method call.
+		//on subsequent calls this will draw only areas that have changed
+		frame.cycle();
+		//NOTE: if frames are linked, only one frame needs to be cycled
+		//		for all frames to update
+	}
+
+	//close out the entire frame tree
+	frame.close();
+
+ */
+
+load("sbbsdefs.js");
+function Frame(x,y,width,height,attr,parent) {
+
+	/* private properties */
+	this.__properties__ = {
+		x:undefined,
+		y:undefined,
+		width:undefined,
+		height:undefined,
+		attr:undefined,
+		display:undefined,
+		data:[],
+		open:false,
+		ctrl_a:false,
+		curr_attr:undefined,
+    attr_stack:[],
+		id:0
+	};
+	this.__settings__ = {
+		v_scroll:true,
+		h_scroll:false,
+		scrollbars:false,
+		lf_strict:true,
+		checkbounds:true,
+		transparent:false,
+		word_wrap:false
+	};
+	this.__relations__ = {
+		parent:undefined,
+		child:[]
+	};
+	this.__position__ = {
+		cursor:undefined,
+		offset:undefined,
+		stored:undefined
+	};
+
+	function init(x,y,width,height,attr,parent) {
+		if(parent instanceof Frame) {
+			this.__properties__.id = parent.display.nextID;
+			this.__properties__.display = parent.display;
+			this.__settings__.checkbounds = parent.checkbounds;
+			this.__relations__.parent = parent;
+			parent.child = this;
+		}
+		else {
+			this.__properties__.display = new Display(x,y,width,height);
+		}
+
+		this.x = x;
+		this.y = y;
+		this.width = width;
+		this.height = height;
+		this.attr = attr;
+
+		this.__position__.cursor = new Cursor(0,0,this);
+		this.__position__.offset = new Offset(0,0,this);
+		this.__position__.stored = new Cursor(0,0,this);
+
+		//log(LOG_DEBUG,format("new frame initialized: %sx%s at %s,%s",this.width,this.height,this.x,this.y));
+	}
+	init.apply(this,arguments);
+}
+
+/* protected properties */
+Frame.prototype.__defineGetter__("child", function() {
+	return this.__relations__.child;
+});
+Frame.prototype.__defineSetter__("child", function(frame) {
+	if(frame instanceof Frame)
+		this.__relations__.child.push(frame);
+	else
+		throw("child not an instance of Frame()");
+});
+Frame.prototype.__defineGetter__("attr", function() {
+	return this.__properties__.attr;
+});
+Frame.prototype.__defineSetter__("attr", function(attr) {
+	if(attr !== undefined && isNaN(attr))
+		throw("invalid attribute: " + attr);
+	this.__properties__.attr = attr;
+});
+Frame.prototype.__defineGetter__("x", function() {
+	if(this.__properties__.x == undefined)
+		return this.__properties__.display.x;
+	return this.__properties__.x;
+});
+Frame.prototype.__defineSetter__("x", function(x) {
+	if(x == undefined)
+		return;
+	if(!this.__checkX__(x))
+		throw("invalid x coordinate: " + x);
+	this.__properties__.x = Number(x);
+});
+Frame.prototype.__defineGetter__("y", function() {
+	if(this.__properties__.y == undefined)
+		return this.__properties__.display.y;
+	return this.__properties__.y;
+});
+Frame.prototype.__defineSetter__("y", function(y) {
+	if(y == undefined)
+		return;
+	if(!this.__checkY__(y))
+		throw("invalid y coordinate: " + y);
+	this.__properties__.y = Number(y);
+});
+Frame.prototype.__defineGetter__("width", function() {
+	if(this.__properties__.width == undefined)
+		return this.__properties__.display.width;
+	return this.__properties__.width;
+});
+Frame.prototype.__defineSetter__("width", function(width) {
+	if(width == undefined)
+		return;
+	if(!this.__checkWidth__(this.x,Number(width)))
+		throw("invalid width: " + width);
+	this.__properties__.width = Number(width);
+});
+Frame.prototype.__defineGetter__("height", function() {
+	if(this.__properties__.height == undefined)
+		return this.__properties__.display.height;
+	return this.__properties__.height;
+});
+Frame.prototype.__defineSetter__("height", function(height) {
+	if(height == undefined)
+		return;
+	if(!this.__checkHeight__(this.y,Number(height)))
+		throw("invalid height: " + height);
+	this.__properties__.height = Number(height);
+});
+
+/* read-only properties */
+Frame.prototype.__defineGetter__("cursor",function() {
+	return this.__position__.cursor;
+});
+Frame.prototype.__defineGetter__("offset",function() {
+	return this.__position__.offset;
+});
+Frame.prototype.__defineGetter__("id", function() {
+	return this.__properties__.id;
+});
+Frame.prototype.__defineGetter__("parent", function() {
+	return this.__relations__.parent;
+});
+Frame.prototype.__defineGetter__("display", function() {
+	return this.__properties__.display;
+});
+Frame.prototype.__defineGetter__("data_height", function() {
+	return this.__properties__.data.length;
+});
+Frame.prototype.__defineGetter__("data_width", function() {
+	if(typeof this.__properties__.data[0] == "undefined")
+		return 0;
+	var longest = 0;
+	for(var d in this.__properties__.data) {
+		if(this.__properties__.data[d].length > longest)
+			longest = this.__properties__.data[d].length;
+	}
+	return longest + 1;
+});
+Frame.prototype.__defineGetter__("data", function() {
+	return this.__properties__.data;
+});
+
+/* protected settings */
+Frame.prototype.__defineGetter__("checkbounds", function() {
+	return this.__settings__.checkbounds;
+});
+Frame.prototype.__defineSetter__("checkbounds", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.checkbounds=bool;
+	else
+		throw("non-boolean checkbounds: " + bool);
+});
+Frame.prototype.__defineGetter__("transparent", function() {
+	return this.__settings__.transparent;
+});
+Frame.prototype.__defineSetter__("transparent", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.transparent=bool;
+	else
+		throw("non-boolean transparent: " + bool);
+});
+Frame.prototype.__defineGetter__("lf_strict", function() {
+	return this.__settings__.lf_strict;
+});
+Frame.prototype.__defineSetter__("lf_strict", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.lf_strict=bool;
+	else
+		throw("non-boolean lf_strict: " + bool);
+});
+Frame.prototype.__defineGetter__("scrollbars", function() {
+	return this.__settings__.scrollbars;
+});
+Frame.prototype.__defineSetter__("scrollbars", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.scrollbars=bool;
+	else
+		throw("non-boolean scrollbars: " + bool);
+});
+Frame.prototype.__defineGetter__("v_scroll", function() {
+	return this.__settings__.v_scroll;
+});
+Frame.prototype.__defineSetter__("v_scroll", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.v_scroll=bool;
+	else
+		throw("non-boolean v_scroll: " + bool);
+});
+Frame.prototype.__defineGetter__("word_wrap", function() {
+	return this.__settings__.word_wrap;
+});
+Frame.prototype.__defineSetter__("word_wrap", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.word_wrap=bool;
+	else
+		throw("non-boolean word_wrap: " + bool);
+});
+Frame.prototype.__defineGetter__("h_scroll", function() {
+	return this.__settings__.h_scroll;
+});
+Frame.prototype.__defineSetter__("h_scroll", function(bool) {
+	if(typeof bool == "boolean")
+		this.__settings__.h_scroll=bool;
+	else
+		throw("non-boolean h_scroll: " + bool);
+});
+Frame.prototype.__defineGetter__("is_open",function() {
+	return this.__properties__.open;
+});
+
+/* public methods */
+Frame.prototype.getData = function(x,y,use_offset) {
+	var px = x;
+	var py = y;
+	if(use_offset) {
+		px += this.__position__.offset.x;
+		py += this.__position__.offset.y;
+	}
+	// if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
+		// throw("Frame.getData() - invalid coordinates: " + px + "," + py);
+	if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
+		return new Char();
+	return this.__properties__.data[py][px];
+}
+Frame.prototype.setData = function(x,y,ch,attr,use_offset) {
+	var px = x;
+	var py = y;
+	if(use_offset) {
+		px += this.__position__.offset.x;
+		py += this.__position__.offset.y;
+	}
+	//I don't remember why I did this, but it was probably important at the time
+	//if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
+		// throw("Frame.setData() - invalid coordinates: " + px + "," + py);
+	if(!this.__properties__.data[py])
+		this.__properties__.data[py] = [];
+	if(!this.__properties__.data[py][px])
+		this.__properties__.data[py][px] = new Char();
+	if(this.__properties__.data[py][px].ch == ch && this.__properties__.data[py][px].attr == attr)
+		return;
+	if(ch)
+		this.__properties__.data[py][px].ch = ch;
+	if(attr)
+		this.__properties__.data[py][px].attr = attr;
+	if(this.__properties__.open)
+		this.__properties__.display.updateChar(this,x,y);
+}
+Frame.prototype.getWord = function(x,y) {
+	var word = []
+	var nx = x-this.x;
+	var ny = y-this.y;
+	var cell = this.getData(nx,ny,false);
+	while(nx >= 0 && cell != undefined && cell.ch != undefined && cell.ch.match(/[0-9a-zA-Z]/)) {
+		word.unshift(cell.ch);
+		nx--;
+		cell = this.getData(nx,ny,false);
+	}
+	nx = x-this.x+1;
+	cell = this.getData(nx,ny,false);
+	while(nx < this.width && cell != undefined && cell.ch != undefined && cell.ch.match(/[0-9a-zA-Z]/)) {
+		word.push(cell.ch);
+		nx++;
+		cell = this.getData(nx,ny,false);
+	}
+	return word.join("");
+}
+Frame.prototype.clearData = function(x,y,use_offset) {
+	var px = x;
+	var py = y;
+	if(use_offset) {
+		px += this.__position__.offset.x;
+		py += this.__position__.offset.y;
+	}
+	if(!this.__properties__.data[py] || !this.__properties__.data[py][px])
+		return;
+	else if(this.__properties__.data[py][px].ch == undefined && this.__properties__.data[py][px].attr == undefined)
+		return;
+	this.__properties__.data[py][px].ch = undefined;
+	this.__properties__.data[py][px].attr = undefined;
+	if(this.__properties__.open)
+		this.__properties__.display.updateChar(this,x,y);
+}
+Frame.prototype.bottom = function() {
+	if(this.__properties__.open) {
+		for each(var c in this.__relations__.child)
+			c.bottom();
+		this.__properties__.display.bottom(this);
+	}
+}
+Frame.prototype.top = function() {
+	if(this.__properties__.open) {
+		this.__properties__.display.top(this);
+		for each(var c in this.__relations__.child)
+			c.top();
+	}
+}
+Frame.prototype.open = function() {
+	if(!this.__properties__.open) {
+		this.__properties__.display.open(this);
+		this.__properties__.open = true;
+	}
+	for each(var c in this.__relations__.child) {
+		c.open();
+	}
+}
+Frame.prototype.refresh = function() {
+	if(this.__properties__.open) {
+		this.__properties__.display.updateFrame(this);
+		for each(var c in this.__relations__.child)
+			c.refresh();
+	}
+}
+Frame.prototype.close = function() {
+	for each(var c in this.__relations__.child)
+		c.close();
+	if(this.__properties__.open) {
+		this.__properties__.display.close(this);
+		this.__properties__.open = false;
+	}
+}
+Frame.prototype.delete = function(id) {
+	if(id == undefined) {
+		this.close();
+		if(this.__relations__.parent) {
+			this.__relations__.parent.delete(this.id);
+		}
+	}
+	else {
+		for(var c=0;c<this.__relations__.child.length;c++) {
+			if(this.__relations__.child[c].id == id) {
+				this.__relations__.child.splice(c--,1);
+				break;
 			}
-		}
-	}
-}
-Frame.prototype.center = function(str,attr) {
-	this.__position__.cursor.x = Math.ceil(this.width/2) - Math.ceil(console.strlen(strip_ctrl(str))/2);
-	if(this.__position__.cursor.x < 0)
-		this.__position__.cursor.x = 0;
-	this.putmsg(str,attr);
-}
-Frame.prototype.gotoxy = function(x,y) {
-	if(typeof x == "object" && x.x && x.y) {
-		if(this.__validateCursor__.call(this,x.x-1,x.y-1)) {
-			this.__position__.cursor.x = x.x-1;
-			this.__position__.cursor.y = x.y-1;
-			return true;
-		}
-		return false;
-	}
-	if(this.__validateCursor__.call(this,x-1,y-1)) {
-		this.__position__.cursor.x = x-1;
-		this.__position__.cursor.y = y-1;
-		return true;
-	}
-	return false;
-}
-Frame.prototype.getxy = function() {
-	return {
-		x:this.__position__.cursor.x+1,
-		y:this.__position__.cursor.y+1
-	};
-}
-Frame.prototype.pushxy = function() {
-	this.__position__.stored.x = this.__position__.cursor.x;
-	this.__position__.stored.y = this.__position__.cursor.y;
-}
-Frame.prototype.popxy = function() {
-	this.__position__.cursor.x = this.__position__.stored.x;
-	this.__position__.cursor.y = this.__position__.stored.y;
-}
-Frame.prototype.up = function(n) {
-	if(this.__position__.cursor.y == 0 && this.__position__.offset.y == 0)
-		return false;
-	if(isNaN(n))
-		n = 1;
-	while(n > 0) {
-		if(this.__position__.cursor.y > 0) {
-			this.__position__.cursor.y--;
-			n--;
-		}
-		else break;
-	}
-	if(n > 0)
-		this.scroll(0,-(n));
-	return true;
-}
-Frame.prototype.down = function(n) {
-	if(this.__position__.cursor.y == this.height-1 && this.__position__.offset.y == this.data_height - this.height)
-		return false;
-	if(isNaN(n))
-		n = 1;
-	while(n > 0) {
-		if(this.__position__.cursor.y < this.height - 1) {
-			this.__position__.cursor.y++;
-			n--;
-		}
-		else break;
-	}
-	if(n > 0)
-		this.scroll(0,n);
-	return true;
-}
-Frame.prototype.left = function(n) {
-	if(this.__position__.cursor.x == 0 && this.__position__.offset.x == 0)
-		return false;
-	if(isNaN(n))
-		n = 1;
-	while(n > 0) {
-		if(this.__position__.cursor.x > 0) {
-			this.__position__.cursor.x--;
-			n--;
-		}
-		else break;
-	}
-	if(n > 0)
-		this.scroll(-(n),0);
-	return true;
-}
-Frame.prototype.right = function(n) {
-	if(this.__position__.cursor.x == this.width-1 && this.__position__.offset.x == this.data_width - this.width)
-		return false;
-	if(isNaN(n))
-		n = 1;
-	while(n > 0) {
-		if(this.__position__.cursor.x < this.width - 1) {
-			this.__position__.cursor.x++;
-			n--;
-		}
-		else break;
-	}
-	if(n > 0)
-		this.scroll(n,0);
-	return true;
-}
-
-/* internal frame methods */
-Frame.prototype.__checkX__ = function(x) {
-	if(	isNaN(x) || (this.__settings__.checkbounds &&
-		(x > this.__properties__.display.x + this.__properties__.display.width ||
-		x < this.__properties__.display.x)))
-		return false;
-	return true;
-}
-Frame.prototype.__checkY__ = function(y) {
-	if( isNaN(y) || (this.__settings__.checkbounds &&
-		(y > this.__properties__.display.y + this.__properties__.display.height ||
-		y < this.__properties__.display.y)))
-		return false;
-	return true;
-}
-Frame.prototype.__checkWidth__ = function(x,width) {
-	if(	width < 1 || isNaN(width) || (this.__settings__.checkbounds &&
-		x + width > this.__properties__.display.x + this.__properties__.display.width))
-		return false;
-	return true;
-}
-Frame.prototype.__checkHeight__ = function(y,height) {
-	if( height < 1 || isNaN(height) || (this.__settings__.checkbounds &&
-		y + height > this.__properties__.display.y + this.__properties__.display.height))
-		return false;
-	return true;
-}
-Frame.prototype.__validateCursor__ = function(x,y) {
-	if(x >= this.width)
-		return false;
-	if(y >= this.height)
-		return false;
-	return true;
-}
-Frame.prototype.__putChar__ = function(ch,attr) {
-	if(this.__position__.cursor.x >= this.width) {
-		this.__position__.cursor.x=0;
-		this.__position__.cursor.y++;
-	}
-	if(this.__position__.cursor.y >= this.height) {
-		this.scroll();
-		this.__position__.cursor.y--;
-	}
-	this.setData(this.__position__.cursor.x,this.__position__.cursor.y,ch,attr,true);
-}
-
-Frame.prototype.__flip__ = function (swaps, flipx) {
-    for (var y = 0; y < this.data_height; y++) {
-        if (!Array.isArray(this.data[y])) continue;
-        if (flipx) this.data[y] = this.data[y].reverse();
-        for (var x = 0; x < this.data[y].length; x++) {
-            for (var s = 0; s < swaps.length; s++) {
-                var i = swaps[s].indexOf(ascii(this.data[y][x].ch));
-                if (i >= 0) {
-                    this.data[y][x].ch = ascii(swaps[s][(i + 1) % 2]);
-                    break;
-                }
-            }
-        }
-    }
-    if (!flipx) this.data.reverse();
-}
-
-Frame.prototype.flipX = function () {
-    const swaps = [
-        [ 40, 41 ],
-        [ 47, 92 ],
-        [ 60, 62 ],
-        [ 91, 93 ],
-        [ 123, 125 ],
-        [ 169, 170 ],
-        [ 174, 175 ],
-        [ 180, 195 ],
-        [ 181, 198 ],
-        [ 182, 199 ],
-        [ 183, 214 ],
-        [ 184, 213 ],
-        [ 185, 204 ],
-        [ 187, 201 ],
-        [ 188, 200 ],
-        [ 189, 211 ],
-        [ 190, 212 ],
-        [ 191, 218 ],
-        [ 192, 217 ],
-        [ 221, 222 ]
-    ];
-    this.__flip__(swaps, true);
-}
-
-Frame.prototype.flipY = function () {
-    const swaps = [
-        [ 47, 92 ],
-        [ 183, 189 ],
-        [ 184, 190 ],
-        [ 187, 188 ],
-        [ 191, 217 ],
-        [ 192, 218 ],
-        [ 193, 194 ],
-        [ 200, 201 ],
-        [ 202, 203 ],
-        [ 207, 209 ],
-        [ 208, 210 ],
-        [ 211, 214 ],
-        [ 212, 213 ],
-        [ 220, 223 ]
-    ];
-    this.__flip__(swaps, false);
-}
-
-/* frame reference object */
-function Canvas(frame,display) {
-	this.frame = frame;
-	this.display = display;
-	this.__defineGetter__("xoff",function() {
-		return this.frame.x - this.display.x;
-	});
-	this.__defineGetter__("yoff",function() {
-		return this.frame.y - this.display.y;
-	});
-}
-
-Canvas.prototype.hasData = function(x,y) {
-	var xpos = x-this.xoff;
-	var ypos = y-this.yoff;
-
-	if(xpos < 0 || ypos < 0)
-		return undefined;
-	if(xpos >= this.frame.width || ypos >= this.frame.height)
-		return undefined;
-	if(this.frame.transparent && this.frame.getData(xpos,ypos,true).ch == undefined)
-		return undefined;
-	return true;
-}
-
-/* object representing screen positional and dimensional limits and canvas stack */
-function Display(x,y,width,height) {
-	/* private properties */
-	this.__properties__ = {
-		x:undefined,
-		y:undefined,
-		width:undefined,
-		height:undefined,
-		canvas:{},
-		update:{},
-		buffer:{},
-		count:0
-	}
-
-	/* initialize display properties */
-	this.x = x;
-	this.y = y;
-	this.width = width;
-	this.height = height;
-	log(LOG_DEBUG,format("new display initialized: %sx%s at %s,%s",this.width,this.height,this.x,this.y));
-}
-
-/* protected display properties */
-Display.prototype.__defineGetter__("x", function() {
-	return this.__properties__.x;
-});
-Display.prototype.__defineSetter__("x", function(x) {
-	if(x == undefined)
-		this.__properties__.x = 1;
-	else if(isNaN(x))
-		throw("invalid x coordinate: " + x);
-	else
-		this.__properties__.x = Number(x);
-});
-Display.prototype.__defineGetter__("y", function() {
-	return this.__properties__.y;
-});
-Display.prototype.__defineSetter__("y", function(y) {
-	if(y == undefined)
-		this.__properties__.y = 1;
-	else if(isNaN(y) || y < 1 || y > console.screen_rows)
-		throw("invalid y coordinate: " + y);
-	else
-		this.__properties__.y = Number(y);
-});
-Display.prototype.__defineGetter__("width", function() {
-	return this.__properties__.width;
-});
-Display.prototype.__defineSetter__("width", function(width) {
-	if(width == undefined)
-		this.__properties__.width = console.screen_columns;
-	else if(isNaN(width) || (this.x + Number(width) - 1) > (console.screen_columns))
-		throw("invalid width: " + width);
-	else
-		this.__properties__.width = Number(width);
-});
-Display.prototype.__defineGetter__("height", function() {
-	return this.__properties__.height;
-});
-Display.prototype.__defineSetter__("height", function(height) {
-	if(height == undefined)
-		this.__properties__.height = console.screen_rows;
-	else if(isNaN(height) || (this.y + Number(height) - 1) > (console.screen_rows))
-		throw("invalid height: " + height);
-	else
-		this.__properties__.height = Number(height);
-});
-Display.prototype.__defineGetter__("nextID",function() {
-	return ++this.__properties__.count;
-});
-
-/* public display methods */
-Display.prototype.cycle = function() {
-	var updates = this.__getUpdateList__();
-	if(updates.length > 0) {
-		var lasty = undefined;
-		var lastx = undefined;
-		var lastf = undefined;
-		for each(var u in updates) {
-			if(lasty !== u.y || lastx == undefined || (u.x - lastx) !== 1)
-				console.gotoxy(u.px,u.py);
-			if(lastf !== u.id)
-				console.attributes = undefined;
-			this.__drawChar__(u.ch,u.attr,u.px,u.py);
-			lastx = u.x;
-			lasty = u.y;
-			lastf = u.id;
-		}
-		//console.attributes=undefined;
-		return true;
-	}
-	return false;
-}
-Display.prototype.draw = function() {
-	for(var y = 0;y<this.height;y++) {
-		for(var x = 0;x<this.width;x++) {
-			this.__updateChar__(x,y);
-		}
-	}
-	this.cycle();
-}
-Display.prototype.open = function(frame) {
-	var canvas = new Canvas(frame,this);
-	this.__properties__.canvas[frame.id] = canvas;
-	this.updateFrame(frame);
-}
-Display.prototype.close = function(frame) {
-	this.updateFrame(frame);
-	delete this.__properties__.canvas[frame.id];
-}
-Display.prototype.top = function(frame) {
-	var canvas = this.__properties__.canvas[frame.id];
-	delete this.__properties__.canvas[frame.id];
-	this.__properties__.canvas[frame.id] = canvas;
-	this.updateFrame(frame);
-}
-Display.prototype.bottom = function(frame) {
-	for(var c in this.__properties__.canvas) {
-		if(c == frame.id)
-			continue;
-		var canvas = this.__properties__.canvas[c];
-		delete this.__properties__.canvas[c];
-		this.__properties__.canvas[c] = canvas;
-	}
-	this.updateFrame(frame);
-}
-Display.prototype.updateFrame = function(frame) {
-	for(var y = 0;y<frame.height;y++) {
-		for(var x = 0;x<frame.width;x++) {
-			this.updateChar(frame,x,y/*,data*/);
-		}
-	}
-}
-Display.prototype.updateChar = function(frame,x,y) {
-	var xoff = frame.x - this.x;
-	var yoff = frame.y - this.y;
-	this.__updateChar__(xoff + x,yoff + y);
-}
-Display.prototype.screenShot = function(file,append) {
-	var f = new File(file);
-	if(append)
-		f.open('ab',true,4096);
-	else
-		f.open('wb',true,4096) ;
-	if(!f.is_open)
-		return false;
-
-	for(var y = 0;y<this.height;y++) {
-		for(var x = 0;x<this.width;x++) {
-			var c = this.__getTopCanvas__(x,y);
-			var d = this.__getData__(c,x,y);
-			if(d.ch)
-				f.write(d.ch);
-			else
-				f.write(" ");
-			if(d.attr)
-				f.writeBin(d.attr,1);
-			else
-				f.writeBin(0,1);
-		}
-	}
-
-	f.close();
-	return true;
-}
-Display.prototype.invalidate = function() {
-	this.__properties__.buffer = {};
-}
-Display.prototype.dump = function() {
-	var arr = [];
-	for(var y = 0; y < this.height; y++) {
-		arr[y] = [];
-		for(var x = 0; x < this.width; x++) {
-			var c = this.__getTopCanvas__(x, y);
-			if(typeof c == "undefined")
-				continue;
-			var d = this.__getData__(c, x, y);
-			if(typeof d.ch != "undefined")
-				arr[y][x] = d;
-		}
-	}
-	return arr;
-}
-
-/* private functions */
-Display.prototype.__updateChar__ = function(x,y/*,data*/) {
-	//ToDo: maybe store char update so I dont have to retrieve it later?
-	if(!this.__properties__.update[y])
-		this.__properties__.update[y] = {};
-	this.__properties__.update[y][x] = 1; /*data; */
-}
-
-Display.prototype.__getUpdateList__ = function() {
-	var list = [];
-	for(var y in this.__properties__.update) {
-		for(var x in this.__properties__.update[y]) {
-			var c = this.__getTopCanvas__(x,y);
-			var d = this.__getData__(c,x,y);
-
-			if(d.px < 1 ||  d.py < 1 || d.px > console.screen_columns || d.py > console.screen_rows)
-				continue;
-			if(!this.__properties__.buffer[x]) {
-				this.__properties__.buffer[x] = {};
-				this.__properties__.buffer[x][y] = d;
-				list.push(d);
-			}
-			else if(this.__properties__.buffer[x][y] == undefined ||
-				this.__properties__.buffer[x][y].ch != d.ch ||
-				this.__properties__.buffer[x][y].attr != d.attr) {
-				this.__properties__.buffer[x][y] = d;
-				list.push(d);
-			}
-		}
-	}
-	this.__properties__.update={};
-	return list.sort(function(a,b) {
-		if(a.y == b.y)
-			return a.x-b.x;
-		return a.y-b.y;
-	});
-}
-Display.prototype.__getData__ = function(c,x,y) {
-	var cd = {
-		x:Number(x),
-		y:Number(y),
-		px:Number(x) + this.__properties__.x,
-		py:Number(y) + this.__properties__.y
-	};
-	if(c) {
-		var d = c.frame.getData(x-c.xoff,y-c.yoff,true);
-		cd.id = c.frame.id;
-		cd.ch = d.ch;
-		if(d.attr)
-			cd.attr = d.attr;
-		else
-			cd.attr = c.frame.attr;
-	}
-	return cd;
-}
-Display.prototype.__drawChar__ = function(ch,attr,xpos,ypos) {
-	if(attr)
-		console.attributes = attr;
-	if(xpos == console.screen_columns && ypos == console.screen_rows)
-		console.cleartoeol();
-	else if(ch == undefined) {
-		console.write(" ");
-	}
-	else {
-		console.write(ch);
-	}
-}
-Display.prototype.__getTopCanvas__ = function(x,y) {
-	var top = undefined;
-	for each(var c in this.__properties__.canvas) {
-		if(c.frame.parent == undefined || c.hasData(x,y))
-			top = c;
-	}
-	return top;
-}
-
-/* character/attribute pair representing a screen position and its contents */
-function Char(ch,attr) {
-	this.ch = ch;
-	this.attr = attr;
-}
-
-/* self-validating cursor position object */
-function Cursor(x,y,frame) {
-	this.__properties__ = {
-		x:undefined,
-		y:undefined,
-		frame:undefined
-	}
-
-	if(frame instanceof Frame)
-		this.__properties__.frame = frame;
-	else
-		throw("the frame is not a frame");
-
-	this.x = x;
-	this.y = y;
-}
-
-Cursor.prototype.__defineGetter__("x", function() {
-	return this.__properties__.x;
-});
-Cursor.prototype.__defineSetter__("x", function(x) {
-	if(isNaN(x))
-		throw("invalid x coordinate: " + x);
-	this.__properties__.x = x;
-});
-Cursor.prototype.__defineGetter__("y", function() {
-	return this.__properties__.y;
-});
-Cursor.prototype.__defineSetter__("y", function(y) {
-	if(isNaN(y))
-		throw("invalid y coordinate: " + y);
-	this.__properties__.y = y;
-});
-
-
-
-/* self-validating scroll offset object */
-function Offset(x,y,frame) {
-	this.__properties__ = {
-		x:undefined,
-		y:undefined,
-		frame:undefined
-	}
-
-	if(frame instanceof Frame)
-		this.__properties__.frame = frame;
-	else
-		throw("the frame is not a frame");
-
-	this.x = x;
-	this.y = y;
-}
-
-Offset.prototype.__defineGetter__("x", function() {
-	return this.__properties__.x;
-});
-Offset.prototype.__defineSetter__("x", function(x) {
-	if(x == undefined)
-		throw("invalid x offset: " + x);
-	else if(x < 0)
-		x = 0;
-	this.__properties__.x = x;
-});
-Offset.prototype.__defineGetter__("y", function() {
-	return this.__properties__.y;
-});
-Offset.prototype.__defineSetter__("y", function(y) {
-	if(y == undefined)
-		throw("invalid y offset: " + y);
-	else if(y < 0)
-		y = 0;
-	this.__properties__.y = y;
-});
+		}
+	}
+}
+Frame.prototype.move = function(x,y) {
+	var nx = undefined;
+	var ny = undefined;
+	if(this.__checkX__(this.x+x) && this.__checkWidth__(this.x+x,this.width))
+		nx = this.x+x;
+	if(this.__checkY__(this.y+y) && this.__checkHeight__(this.y+y,this.height))
+		ny = this.y+y;
+	if(nx == undefined && ny == undefined)
+		return;
+	this.__properties__.display.updateFrame(this);
+	if(nx !== undefined)
+		this.x=nx;
+	if(ny !== undefined)
+		this.y=ny;
+	this.__properties__.display.updateFrame(this);
+	for each(var c in this.__relations__.child)
+		c.move(x,y);
+}
+Frame.prototype.moveTo = function(x,y) {
+	for each(var c in this.__relations__.child) {
+		var cx = (x + (c.x-this.x));
+		var cy = (y + (c.y-this.y));
+		c.moveTo(cx,cy);
+	}
+	var nx = undefined;
+	var ny = undefined;
+	if(this.__checkX__(x))
+		nx = x;
+	if(this.__checkY__(y))
+		ny = y;
+	if(nx == undefined && ny == undefined)
+		return;
+	this.__properties__.display.updateFrame(this);
+	if(nx !== undefined)
+		this.x=nx;
+	if(ny !== undefined)
+		this.y=ny;
+	this.__properties__.display.updateFrame(this);
+}
+Frame.prototype.draw = function() {
+	if(this.__properties__.open)
+		this.refresh();
+	else
+		this.open();
+	this.cycle();
+}
+Frame.prototype.cycle = function() {
+	return this.__properties__.display.cycle();
+}
+Frame.prototype.load = function(filename,width,height) {
+	var f=new File(filename);
+	if (!f.open("rb", true))
+		return false;
+	var contents=f.read();
+	f.close();
+	var valid_sauce = false;
+	var ext = file_getext(filename).substr(1).toUpperCase();
+
+	if (contents.substr(-128, 7) == "SAUCE00") {
+		var sauceless_size = ascii(contents.substr(-35,1));
+		sauceless_size <<= 8;
+		sauceless_size |= ascii(contents.substr(-36,1));
+		sauceless_size <<= 8;
+		sauceless_size |= ascii(contents.substr(-37,1));
+		sauceless_size <<= 8;
+		sauceless_size |= ascii(contents.substr(-38,1));
+
+		var data_type = ascii(contents.substr(-34,1));
+		var file_type = ascii(contents.substr(-33,1));
+		var tinfo1 = ascii(contents.substr(-31,1));
+		tinfo1 <<= 8;
+		tinfo1 |= ascii(contents.substr(-32,1));
+		var tinfo2 = ascii(contents.substr(-29,1));
+		tinfo2 <<= 8;
+		tinfo2 |= ascii(contents.substr(-30,1));
+		switch(data_type) {
+			case 1:
+				switch(file_type) {
+					case 0:	// PLain ASCII
+						ext = "TXT";
+						if (tinfo1)
+							width = tinfo1;
+						if (tinfo2)
+							height = tinfo2;
+						break;
+					case 1: // ANSi
+						ext = "ANS";
+						if (tinfo1)
+							width = tinfo1;
+						if (tinfo2)
+							height = tinfo2;
+						break;
+					case 7: // Source
+						ext = "TXT";
+						break;
+				}
+				valid_sauce = true;
+				break;
+			case 5:
+				ext = 'BIN';
+				width = file_type * 2;
+				height = (sauceless_size / 2) / width;
+				valid_sauce = true;
+				break;
+		}
+		if (valid_sauce)
+			contents = contents.substr(0, sauceless_size);
+	}
+
+	switch(ext) {
+	case "ANS":
+		/*
+		 * TODO: This doesn't do exactly what reading a text file does
+		 * one Windows (nor on Linux), but it should be close to what
+		 * was meant, and should work given that it resets x every line.
+		 */
+		var lines=contents.split(/\r\n/);
+
+		var attr = this.attr;
+		var bg = BG_BLACK;
+		var fg = LIGHTGRAY;
+
+		var i = 0;
+		var y = 0;
+		var saved = {};
+
+		while(lines.length > 0) {
+			var x = 0;
+			var line = lines.shift();
+			/* parse 'ATCODES'??
+			line = line.replace(/@(.*)@/g,
+				function (str, code, offset, s) {
+					return bbs.atcode(code);
+				}
+			);
+			*/
+			while(line.length > 0) {
+				/* parse an attribute sequence*/
+				var m = line.match(/^\x1b\[(\d*);?(\d*);?(\d*)m/);
+				if(m !== null) {
+					line = line.substr(m.shift().length);
+					if(m[0] == 0) {
+						bg = BG_BLACK;
+						fg = LIGHTGRAY;
+						i = 0;
+						m.shift();
+					}
+					if(m[0] == 1) {
+						i = HIGH;
+						m.shift();
+					}
+					if(m[0] >= 40) {
+						switch(Number(m.shift())) {
+						case 40:
+							bg = BG_BLACK;
+							break;
+						case 41:
+							bg = BG_RED;
+							break;
+						case 42:
+							bg = BG_GREEN;
+							break;
+						case 43:
+							bg = BG_BROWN;
+							break;
+						case 44:
+							bg = BG_BLUE;
+							break;
+						case 45:
+							bg = BG_MAGENTA;
+							break;
+						case 46:
+							bg = BG_CYAN;
+							break;
+						case 47:
+							bg = BG_LIGHTGRAY;
+							break;
+						}
+					}
+					if(m[0] >= 30) {
+						switch(Number(m.shift())) {
+						case 30:
+							fg = BLACK;
+							break;
+						case 31:
+							fg = RED;
+							break;
+						case 32:
+							fg = GREEN;
+							break;
+						case 33:
+							fg = BROWN;
+							break;
+						case 34:
+							fg = BLUE;
+							break;
+						case 35:
+							fg = MAGENTA;
+							break;
+						case 36:
+							fg = CYAN;
+							break;
+						case 37:
+							fg = LIGHTGRAY;
+							break;
+						}
+					}
+					attr = bg + fg + i;
+					continue;
+				}
+
+				/* parse absolute character position */
+				var m = line.match(/^\x1b\[(\d*);?(\d*)[Hf]/);
+				if(m !== null) {
+					line = line.substr(m.shift().length);
+
+					if(m.length==0) {
+						x=0;
+						y=0;
+					}
+					else {
+						if(m[0])
+							y = Number(m.shift())-1;
+						if(m[0])
+							x = Number(m.shift())-1;
+					}
+					continue;
+				}
+
+				/* ignore a bullshit sequence */
+				var n = line.match(/^\x1b\[\?7h/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					continue;
+				}
+
+				/* parse an up positional sequence */
+				var n = line.match(/^\x1b\[(\d*)A/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					var chars = n.shift();
+					if(chars < 1)
+						y-=1;
+					else
+						y-=Number(chars);
+					continue;
+				}
+
+				/* parse a down positional sequence */
+				var n = line.match(/^\x1b\[(\d*)B/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					var chars = n.shift();
+					if(chars < 1)
+						y+=1;
+					else
+						y+=Number(chars);
+					continue;
+				}
+
+				/* parse a forward positional sequence */
+				var n = line.match(/^\x1b\[(\d*)C/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					var chars = n.shift();
+					if(chars < 1)
+						x+=1;
+					else
+						x+=Number(chars);
+					continue;
+				}
+
+				/* parse a backward positional sequence */
+				var n = line.match(/^\x1b\[(\d*)D/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					var chars = n.shift()
+					if(chars < 1)
+						x-=1;
+					else
+						x-=Number(chars);
+					continue;
+				}
+
+				/* parse a clear screen sequence */
+				var n = line.match(/^\x1b\[2J/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					continue;
+				}
+
+				/* parse save cursor sequence */
+				var n = line.match(/^\x1b\[s/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					saved.x = x;
+					saved.y = y;
+					continue;
+				}
+
+				/* parse restore cursor sequence */
+				var n = line.match(/^\x1b\[u/);
+				if(n !== null) {
+					line = line.substr(n.shift().length);
+					x = saved.x;
+					y = saved.y;
+					continue;
+				}
+
+				/* set character and attribute */
+				var ch = line[0];
+				line = line.substr(1);
+
+				/* validate position */
+				if(y<0)
+					y=0;
+				if(x<0)
+					x=0;
+				if(x>=this.width) {
+					x=0;
+					y++;
+				}
+				/* set character and attribute */
+				if(!this.__properties__.data[y])
+					this.__properties__.data[y]=[];
+				this.__properties__.data[y][x]=new Char(ch,attr);
+				x++;
+			}
+			y++;
+		}
+		break;
+    case "BIN":
+        if (!this.load_bin(contents, width, height, 0)) return false;
+		break;
+	case "TXT":
+		var lines=contents.split(/\r\n/);
+		while(lines.length > 0)
+			this.putmsg(lines.shift() + "\r\n");
+		break;
+	default:
+		throw("unsupported filetype");
+		break;
+	}
+}
+Frame.prototype.load_bin = function(contents, width, height, offset) {
+    if(width == undefined || height == undefined)
+        throw("unknown graphic dimensions");
+    if(offset == undefined) offset = 0;
+    for(var y=0; y<height; y++) {
+        for(var x=0; x<width; x++) {
+            var c = new Char();
+            if(offset >= contents.length)
+                return(false);
+            c.ch = contents.substr(offset++, 1);
+            if(offset == contents.length)
+                return(false);
+            c.attr = ascii(contents.substr(offset++, 1));
+            c.id = this.id;
+            if(!this.__properties__.data[y])
+                this.__properties__.data[y]=[];
+            this.__properties__.data[y][x] = c;
+        }
+    }
+    return true;
+}
+Frame.prototype.scroll = function(x,y) {
+	var update = false;
+	/* default: add a new line to the data matrix */
+	if(x == undefined && y == undefined) {
+		if(this.__settings__.v_scroll) {
+			var newrow = [];
+			for(var x = 0;x<this.width;x++) {
+				for(var y = 0;y<this.height;y++)
+					this.__properties__.display.updateChar(this,x,y);
+				newrow.push(new Char());
+			}
+			this.__properties__.data.push(newrow);
+			this.__position__.offset.y++;
+			update = true;
+		}
+	}
+	/* otherwise, adjust the x/y offset */
+	else {
+		if(typeof x == "number" && x !== 0 && this.__settings__.h_scroll) {
+			this.__position__.offset.x += x;
+			update = true;
+		}
+		if(typeof y == "number" && y !== 0 && this.__settings__.v_scroll) {
+			this.__position__.offset.y += y;
+			update = true;
+		}
+		if(update)
+			this.refresh();
+	}
+	return update;
+}
+Frame.prototype.scrollTo = function(x,y) {
+	var update = false;
+	if(typeof x == "number") {
+		if(this.__settings__.h_scroll) {
+			this.__position__.offset.x = x;
+			update = true;
+		}
+	}
+	if(typeof y == "number") {
+		if(this.__settings__.v_scroll) {
+			this.__position__.offset.y = y;
+			update = true;
+		}
+	}
+	if(update)
+		this.refresh();
+}
+Frame.prototype.insertLine = function(y) {
+	if(this.__properties__.data[y])
+		this.__properties__.data.splice(y,0,[]);
+	else
+		this.__properties__.data[y] = [];
+	this.refresh();
+	return this.__properties__.data[y];
+}
+Frame.prototype.deleteLine = function(y) {
+	var l = undefined;
+	if(this.__properties__.data[y]) {
+		l = this.__properties__.data.splice(y,1);
+		this.refresh();
+	}
+	return l;
+}
+Frame.prototype.screenShot = function(file,append) {
+	return this.__properties__.display.screenShot(file,append);
+}
+Frame.prototype.invalidate = function() {
+	this.__properties__.display.invalidate();
+	this.refresh();
+}
+Frame.prototype.dump = function() {
+	return this.__properties__.display.dump();
+}
+
+/* console method emulation */
+Frame.prototype.home = function() {
+	this.__position__.cursor.x = 0;
+	this.__position__.cursor.y = 0;
+	return true;
+}
+Frame.prototype.end = function() {
+	this.__position__.cursor.x = this.width-1;
+	this.__position__.cursor.y = this.height-1;
+	return true;
+}
+Frame.prototype.pagedown = function() {
+	this.__position__.offset.y += this.height-1;
+	if(this.__position__.offset.y >= this.data_height)
+		this.__position__.offset.y = this.data_height - this.height;
+	this.refresh();
+}
+Frame.prototype.pageup = function() {
+	this.__position__.offset.y -= this.height-1;
+	if(this.__position__.offset.y < 0)
+		this.__position__.offset.y = 0;
+	this.refresh();
+}
+Frame.prototype.clear = function (attr) {
+	if (attr) this.attr = attr;
+	this.__properties__.data = [];
+	this.__position__.offset.x = 0;
+	this.__position__.offset.y = 0;
+	this.home();
+	this.invalidate();
+}
+Frame.prototype.erase = function(ch, attr) {
+	if(attr == undefined)
+		attr = this.attr;
+	var px = this.__position__.offset.x;
+	var py = this.__position__.offset.y;
+	for(var y = 0; y< this.height; y++) {
+		if(!this.__properties__.data[py + y]) {
+			continue;
+		}
+		for(var x = 0; x<this.width; x++) {
+			if(!this.__properties__.data[py + y][px + x]) {
+				continue;
+			}
+			if((this.__properties__.data[py + y][px + x].ch === undefined || 
+				this.__properties__.data[py + y][px + x].ch === ch) && 
+				this.__properties__.data[py + y][px + x].attr == attr) {
+				continue;
+			}
+			this.__properties__.data[py + y][px + x].ch = undefined;
+			this.__properties__.data[py + y][px + x].attr = attr;
+			this.__properties__.display.updateChar(this, x, y);
+		}
+	}
+	this.home();
+}
+Frame.prototype.clearline = function(attr) {
+	if(attr == undefined)
+		attr = this.attr;
+	var py = this.__position__.offset.y + this.__position__.cursor.y;
+	if(!this.__properties__.data[py])
+		return false;
+	for(var x=0;x<this.__properties__.data[py].length;x++) {
+		if(this.__properties__.data[py][x]) {
+			this.__properties__.data[py][x].ch = undefined;
+			this.__properties__.data[py][x].attr = attr;
+		}
+	}
+	for(var x=0;x<this.width;x++) {
+		this.__properties__.display.updateChar(this,x,this.__position__.cursor.y);
+	}
+}
+Frame.prototype.cleartoeol = function(attr) {
+	if(attr == undefined)
+		attr = this.attr;
+	var px = this.__position__.offset.x + this.__position__.cursor.x;
+	var py = this.__position__.offset.y + this.__position__.cursor.y;
+	if(!this.__properties__.data[this.__position__.cursor.y])
+		return false;
+	for(var x=px;x<this.__properties__.data[py].length;x++) {
+		if(this.__properties__.data[py][x]) {
+			if(this.__properties__.data[py][x].ch !== undefined) {
+				this.__properties__.data[py][x].ch = undefined;
+				this.__properties__.display.updateChar(this,x - this.__position__.offset.x, this.__position__.cursor.y);
+			}
+			this.__properties__.data[py][x].attr = attr;
+		}
+	}
+}
+Frame.prototype.crlf = function() {
+	this.__position__.cursor.x = 0;
+	if(this.__position__.cursor.y < this.height-1)
+		this.__position__.cursor.y += 1;
+	else {}
+}
+Frame.prototype.write = function(str,attr) {
+	if(str == undefined)
+		return;
+	if(this.__settings__.word_wrap)
+		str = word_wrap(str, this.width,str.length, false);
+	str = str.toString().split('');
+
+	if(attr)
+		this.__properties__.curr_attr = attr;
+	else
+		this.__properties__.curr_attr = this.attr;
+	var pos = this.__position__.cursor;
+	while(str.length > 0) {
+		var ch = str.shift();
+		this.__putChar__.call(this,ch,this.__properties__.curr_attr);
+		pos.x++;
+	}
+}
+Frame.prototype.putmsg = function(str,attr) {
+	if(str == undefined)
+		return;
+	if(this.__settings__.word_wrap) {
+		var remainingWidth = this.width - this.__position__.cursor.x;
+		if(str.length > remainingWidth) {
+			str = word_wrap(str,remainingWidth,str.length,false).split('\n');
+			var trailing_newline = str[str.length - 1].length == 0;
+			str = str.shift() + '\r\n' + word_wrap(str.join('\r\n'),this.width,remainingWidth,false);
+			if(!trailing_newline) str = str.trimRight();
+		}
+	}
+	str = str.toString().split('');
+
+	if(attr)
+		this.__properties__.curr_attr = attr;
+	else
+		this.__properties__.curr_attr = this.attr;
+	var pos = this.__position__.cursor;
+
+	while(str.length > 0) {
+		var ch = str.shift();
+		if(this.__properties__.ctrl_a) {
+			var k = ch;
+			if(k)
+				k = k.toUpperCase();
+			switch(k) {
+			case '\1':	/* A "real" ^A code */
+				this.__putChar__.call(this,ch,this.__properties__.curr_attr);
+				pos.x++;
+				break;
+			case 'K':	/* Black */
+				this.__properties__.curr_attr=(this.__properties__.curr_attr)&0xf8;
+				break;
+			case 'R':	/* Red */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|RED;
+				break;
+			case 'G':	/* Green */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|GREEN;
+				break;
+			case 'Y':	/* Yellow */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|BROWN;
+				break;
+			case 'B':	/* Blue */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|BLUE;
+				break;
+			case 'M':	/* Magenta */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|MAGENTA;
+				break;
+			case 'C':	/* Cyan */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|CYAN;
+				break;
+			case 'W':	/* White */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0xf8)|LIGHTGRAY;
+				break;
+			case '0':	/* Black */
+				this.__properties__.curr_attr=(this.__properties__.curr_attr)&0x8f;
+				break;
+			case '1':	/* Red */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(RED<<4);
+				break;
+			case '2':	/* Green */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(GREEN<<4);
+				break;
+			case '3':	/* Yellow */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(BROWN<<4);
+				break;
+			case '4':	/* Blue */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(BLUE<<4);
+				break;
+			case '5':	/* Magenta */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(MAGENTA<<4);
+				break;
+			case '6':	/* Cyan */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(CYAN<<4);
+				break;
+			case '7':	/* White */
+				this.__properties__.curr_attr=((this.__properties__.curr_attr)&0x8f)|(LIGHTGRAY<<4);
+				break;
+			case 'H':	/* High Intensity */
+				this.__properties__.curr_attr|=HIGH;
+				break;
+			case 'I':	/* Blink */
+				this.__properties__.curr_attr|=BLINK;
+				break;
+			case 'N': 	/* Normal */
+				this.__properties__.curr_attr=(this.attr);
+				break;
+			case '+':
+				this.__properties__.attr_stack.push(this.__properties__.curr_attr);
+				break;
+			case '-':	/* Normal if High, Blink, or BG */
+				if (this.__properties__.attr_stack.length) {
+				  this.__properties__.curr_attr = this.__properties__.attr_stack.pop();
+				} else if(this.__properties__.curr_attr & 0xf8) {
+							this.__properties__.curr_attr=this.attr;
+				}
+				break;
+			case '_':	/* Normal if blink/background */
+				if(this.__properties__.curr_attr & 0xf0)
+					this.__properties__.curr_attr=this.attr;
+				break;
+			case '[':	/* CR */
+				pos.x=0;
+				break;
+			case ']':	/* LF */
+				pos.y++;
+				if(this.__settings__.lf_strict && pos.y >= this.height) {
+					this.scroll();
+					pos.y--;
+				}
+				break;
+			default:	/* Other stuff... specifically, check for right movement */
+				if(ch.charCodeAt(0)>127)
+					pos.x+=ch.charCodeAt(0)-127;
+				break;
+			}
+			this.__properties__.ctrl_a = false;
+		}
+		else {
+			switch(ch) {
+			case '\1':		/* CTRL-A code */
+			case ctrl('A'):
+				this.__properties__.ctrl_a = true;
+				break;
+			case '\7':		/* Beep */
+				break;
+			case '\x7f':	/* DELETE */
+				break;
+			case '\b':
+				if(pos.x > 0) {
+					pos.x--;
+					this.__putChar__.call(this," ",this.__properties__.curr_attr);
+				}
+				break;
+			case '\r':
+				pos.x=0;
+				break;
+			case '\t':
+				pos.x += 4;
+				break;
+			case '\n':
+				pos.y++;
+				if(this.__settings__.lf_strict && pos.y >= this.height) {
+					this.scroll();
+					pos.y--;
+				}
+				break;
+			default:
+				this.__putChar__.call(this,ch,this.__properties__.curr_attr);
+				pos.x++;
+				break;
+			}

+		}
+	}
+}
+Frame.prototype.center = function(str,attr) {
+	this.__position__.cursor.x = Math.ceil(this.width/2) - Math.ceil(console.strlen(strip_ctrl(str))/2);
+	if(this.__position__.cursor.x < 0)
+		this.__position__.cursor.x = 0;
+	this.putmsg(str,attr);
+}
+Frame.prototype.gotoxy = function(x,y) {
+	if(typeof x == "object" && x.x && x.y) {
+		if(this.__validateCursor__.call(this,x.x-1,x.y-1)) {
+			this.__position__.cursor.x = x.x-1;
+			this.__position__.cursor.y = x.y-1;
+			return true;
+		}
+		return false;
+	}
+	if(this.__validateCursor__.call(this,x-1,y-1)) {
+		this.__position__.cursor.x = x-1;
+		this.__position__.cursor.y = y-1;
+		return true;
+	}
+	return false;
+}
+Frame.prototype.getxy = function() {
+	return {
+		x:this.__position__.cursor.x+1,
+		y:this.__position__.cursor.y+1
+	};
+}
+Frame.prototype.pushxy = function() {
+	this.__position__.stored.x = this.__position__.cursor.x;
+	this.__position__.stored.y = this.__position__.cursor.y;
+}
+Frame.prototype.popxy = function() {
+	this.__position__.cursor.x = this.__position__.stored.x;
+	this.__position__.cursor.y = this.__position__.stored.y;
+}
+Frame.prototype.up = function(n) {
+	if(this.__position__.cursor.y == 0 && this.__position__.offset.y == 0)
+		return false;
+	if(isNaN(n))
+		n = 1;
+	while(n > 0) {
+		if(this.__position__.cursor.y > 0) {
+			this.__position__.cursor.y--;
+			n--;
+		}
+		else break;
+	}
+	if(n > 0)
+		this.scroll(0,-(n));
+	return true;
+}
+Frame.prototype.down = function(n) {
+	if(this.__position__.cursor.y == this.height-1 && this.__position__.offset.y == this.data_height - this.height)
+		return false;
+	if(isNaN(n))
+		n = 1;
+	while(n > 0) {
+		if(this.__position__.cursor.y < this.height - 1) {
+			this.__position__.cursor.y++;
+			n--;
+		}
+		else break;
+	}
+	if(n > 0)
+		this.scroll(0,n);
+	return true;
+}
+Frame.prototype.left = function(n) {
+	if(this.__position__.cursor.x == 0 && this.__position__.offset.x == 0)
+		return false;
+	if(isNaN(n))
+		n = 1;
+	while(n > 0) {
+		if(this.__position__.cursor.x > 0) {
+			this.__position__.cursor.x--;
+			n--;
+		}
+		else break;
+	}
+	if(n > 0)
+		this.scroll(-(n),0);
+	return true;
+}
+Frame.prototype.right = function(n) {
+	if(this.__position__.cursor.x == this.width-1 && this.__position__.offset.x == this.data_width - this.width)
+		return false;
+	if(isNaN(n))
+		n = 1;
+	while(n > 0) {
+		if(this.__position__.cursor.x < this.width - 1) {
+			this.__position__.cursor.x++;
+			n--;
+		}
+		else break;
+	}
+	if(n > 0)
+		this.scroll(n,0);
+	return true;
+}
+
+/* internal frame methods */
+Frame.prototype.__checkX__ = function(x) {
+	if(	isNaN(x) || (this.__settings__.checkbounds &&
+		(x > this.__properties__.display.x + this.__properties__.display.width ||
+		x < this.__properties__.display.x)))
+		return false;
+	return true;
+}
+Frame.prototype.__checkY__ = function(y) {
+	if( isNaN(y) || (this.__settings__.checkbounds &&
+		(y > this.__properties__.display.y + this.__properties__.display.height ||
+		y < this.__properties__.display.y)))
+		return false;
+	return true;
+}
+Frame.prototype.__checkWidth__ = function(x,width) {
+	if(	width < 1 || isNaN(width) || (this.__settings__.checkbounds &&
+		x + width > this.__properties__.display.x + this.__properties__.display.width))
+		return false;
+	return true;
+}
+Frame.prototype.__checkHeight__ = function(y,height) {
+	if( height < 1 || isNaN(height) || (this.__settings__.checkbounds &&
+		y + height > this.__properties__.display.y + this.__properties__.display.height))
+		return false;
+	return true;
+}
+Frame.prototype.__validateCursor__ = function(x,y) {
+	if(x >= this.width)
+		return false;
+	if(y >= this.height)
+		return false;
+	return true;
+}
+Frame.prototype.__putChar__ = function(ch,attr) {
+	if(this.__position__.cursor.x >= this.width) {
+		this.__position__.cursor.x=0;
+		this.__position__.cursor.y++;
+	}
+	if(this.__position__.cursor.y >= this.height) {
+		this.scroll();
+		this.__position__.cursor.y--;
+	}
+	this.setData(this.__position__.cursor.x,this.__position__.cursor.y,ch,attr,true);
+}
+
+Frame.prototype.__flip__ = function (swaps, flipx) {
+    for (var y = 0; y < this.data_height; y++) {
+        if (!Array.isArray(this.data[y])) continue;
+        if (flipx) this.data[y] = this.data[y].reverse();
+        for (var x = 0; x < this.data[y].length; x++) {
+            for (var s = 0; s < swaps.length; s++) {
+                var i = swaps[s].indexOf(ascii(this.data[y][x].ch));
+                if (i >= 0) {
+                    this.data[y][x].ch = ascii(swaps[s][(i + 1) % 2]);
+                    break;
+                }
+            }
+        }
+    }
+    if (!flipx) this.data.reverse();
+}
+
+Frame.prototype.flipX = function () {
+    const swaps = [
+        [ 40, 41 ],
+        [ 47, 92 ],
+        [ 60, 62 ],
+        [ 91, 93 ],
+        [ 123, 125 ],
+        [ 169, 170 ],
+        [ 174, 175 ],
+        [ 180, 195 ],
+        [ 181, 198 ],
+        [ 182, 199 ],
+        [ 183, 214 ],
+        [ 184, 213 ],
+        [ 185, 204 ],
+        [ 187, 201 ],
+        [ 188, 200 ],
+        [ 189, 211 ],
+        [ 190, 212 ],
+        [ 191, 218 ],
+        [ 192, 217 ],
+        [ 221, 222 ]
+    ];
+    this.__flip__(swaps, true);
+}
+
+Frame.prototype.flipY = function () {
+    const swaps = [
+        [ 47, 92 ],
+        [ 183, 189 ],
+        [ 184, 190 ],
+        [ 187, 188 ],
+        [ 191, 217 ],
+        [ 192, 218 ],
+        [ 193, 194 ],
+        [ 200, 201 ],
+        [ 202, 203 ],
+        [ 207, 209 ],
+        [ 208, 210 ],
+        [ 211, 214 ],
+        [ 212, 213 ],
+        [ 220, 223 ]
+    ];
+    this.__flip__(swaps, false);
+}
+
+/* frame reference object */
+function Canvas(frame,display) {
+	this.frame = frame;
+	this.display = display;
+	this.__defineGetter__("xoff",function() {
+		return this.frame.x - this.display.x;
+	});
+	this.__defineGetter__("yoff",function() {
+		return this.frame.y - this.display.y;
+	});
+}
+
+Canvas.prototype.hasData = function(x,y) {
+	var xpos = x-this.xoff;
+	var ypos = y-this.yoff;
+
+	if(xpos < 0 || ypos < 0)
+		return undefined;
+	if(xpos >= this.frame.width || ypos >= this.frame.height)
+		return undefined;
+	if(this.frame.transparent && this.frame.getData(xpos,ypos,true).ch == undefined)
+		return undefined;
+	return true;
+}
+
+/* object representing screen positional and dimensional limits and canvas stack */
+function Display(x,y,width,height) {
+	/* private properties */
+	this.__properties__ = {
+		x:undefined,
+		y:undefined,
+		width:undefined,
+		height:undefined,
+		canvas:{},
+		update:{},
+		buffer:{},
+		count:0
+	}
+
+	/* initialize display properties */
+	this.x = x;
+	this.y = y;
+	this.width = width;
+	this.height = height;
+	log(LOG_DEBUG,format("new display initialized: %sx%s at %s,%s",this.width,this.height,this.x,this.y));
+}
+
+/* protected display properties */
+Display.prototype.__defineGetter__("x", function() {
+	return this.__properties__.x;
+});
+Display.prototype.__defineSetter__("x", function(x) {
+	if(x == undefined)
+		this.__properties__.x = 1;
+	else if(isNaN(x))
+		throw("invalid x coordinate: " + x);
+	else
+		this.__properties__.x = Number(x);
+});
+Display.prototype.__defineGetter__("y", function() {
+	return this.__properties__.y;
+});
+Display.prototype.__defineSetter__("y", function(y) {
+	if(y == undefined)
+		this.__properties__.y = 1;
+	else if(isNaN(y) || y < 1 || y > console.screen_rows)
+		throw("invalid y coordinate: " + y);
+	else
+		this.__properties__.y = Number(y);
+});
+Display.prototype.__defineGetter__("width", function() {
+	return this.__properties__.width;
+});
+Display.prototype.__defineSetter__("width", function(width) {
+	if(width == undefined)
+		this.__properties__.width = console.screen_columns;
+	else if(isNaN(width) || (this.x + Number(width) - 1) > (console.screen_columns))
+		throw("invalid width: " + width);
+	else
+		this.__properties__.width = Number(width);
+});
+Display.prototype.__defineGetter__("height", function() {
+	return this.__properties__.height;
+});
+Display.prototype.__defineSetter__("height", function(height) {
+	if(height == undefined)
+		this.__properties__.height = console.screen_rows;
+	else if(isNaN(height) || (this.y + Number(height) - 1) > (console.screen_rows))
+		throw("invalid height: " + height);
+	else
+		this.__properties__.height = Number(height);
+});
+Display.prototype.__defineGetter__("nextID",function() {
+	return ++this.__properties__.count;
+});
+
+/* public display methods */
+Display.prototype.cycle = function() {
+	var updates = this.__getUpdateList__();
+	if(updates.length > 0) {
+		var lasty = undefined;
+		var lastx = undefined;
+		var lastf = undefined;
+		for each(var u in updates) {
+			if(lasty !== u.y || lastx == undefined || (u.x - lastx) !== 1)
+				console.gotoxy(u.px,u.py);
+			if(lastf !== u.id)
+				console.attributes = undefined;
+			this.__drawChar__(u.ch,u.attr,u.px,u.py);
+			lastx = u.x;
+			lasty = u.y;
+			lastf = u.id;
+		}
+		//console.attributes=undefined;
+		return true;
+	}
+	return false;
+}
+Display.prototype.draw = function() {
+	for(var y = 0;y<this.height;y++) {
+		for(var x = 0;x<this.width;x++) {
+			this.__updateChar__(x,y);
+		}
+	}
+	this.cycle();
+}
+Display.prototype.open = function(frame) {
+	var canvas = new Canvas(frame,this);
+	this.__properties__.canvas[frame.id] = canvas;
+	this.updateFrame(frame);
+}
+Display.prototype.close = function(frame) {
+	this.updateFrame(frame);
+	delete this.__properties__.canvas[frame.id];
+}
+Display.prototype.top = function(frame) {
+	var canvas = this.__properties__.canvas[frame.id];
+	delete this.__properties__.canvas[frame.id];
+	this.__properties__.canvas[frame.id] = canvas;
+	this.updateFrame(frame);
+}
+Display.prototype.bottom = function(frame) {
+	for(var c in this.__properties__.canvas) {
+		if(c == frame.id)
+			continue;
+		var canvas = this.__properties__.canvas[c];
+		delete this.__properties__.canvas[c];
+		this.__properties__.canvas[c] = canvas;
+	}
+	this.updateFrame(frame);
+}
+Display.prototype.updateFrame = function(frame) {
+	for(var y = 0;y<frame.height;y++) {
+		for(var x = 0;x<frame.width;x++) {
+			this.updateChar(frame,x,y/*,data*/);
+		}
+	}
+}
+Display.prototype.updateChar = function(frame,x,y) {
+	var xoff = frame.x - this.x;
+	var yoff = frame.y - this.y;
+	this.__updateChar__(xoff + x,yoff + y);
+}
+Display.prototype.screenShot = function(file,append) {
+	var f = new File(file);
+	if(append)
+		f.open('ab',true,4096);
+	else
+		f.open('wb',true,4096) ;
+	if(!f.is_open)
+		return false;
+
+	for(var y = 0;y<this.height;y++) {
+		for(var x = 0;x<this.width;x++) {
+			var c = this.__getTopCanvas__(x,y);
+			var d = this.__getData__(c,x,y);
+			if(d.ch)
+				f.write(d.ch);
+			else
+				f.write(" ");
+			if(d.attr)
+				f.writeBin(d.attr,1);
+			else
+				f.writeBin(0,1);
+		}
+	}
+
+	f.close();
+	return true;
+}
+Display.prototype.invalidate = function() {
+	this.__properties__.buffer = {};
+}
+Display.prototype.dump = function() {
+	var arr = [];
+	for(var y = 0; y < this.height; y++) {
+		arr[y] = [];
+		for(var x = 0; x < this.width; x++) {
+			var c = this.__getTopCanvas__(x, y);
+			if(typeof c == "undefined")
+				continue;
+			var d = this.__getData__(c, x, y);
+			if(typeof d.ch != "undefined")
+				arr[y][x] = d;
+		}
+	}
+	return arr;
+}
+
+/* private functions */
+Display.prototype.__updateChar__ = function(x,y/*,data*/) {
+	//ToDo: maybe store char update so I dont have to retrieve it later?
+	if(!this.__properties__.update[y])
+		this.__properties__.update[y] = {};
+	this.__properties__.update[y][x] = 1; /*data; */
+}
+
+Display.prototype.__getUpdateList__ = function() {
+	var list = [];
+	for(var y in this.__properties__.update) {
+		for(var x in this.__properties__.update[y]) {
+			var c = this.__getTopCanvas__(x,y);
+			var d = this.__getData__(c,x,y);
+
+			if(d.px < 1 ||  d.py < 1 || d.px > console.screen_columns || d.py > console.screen_rows)
+				continue;
+			if(!this.__properties__.buffer[x]) {
+				this.__properties__.buffer[x] = {};
+				this.__properties__.buffer[x][y] = d;
+				list.push(d);
+			}
+			else if(this.__properties__.buffer[x][y] == undefined ||
+				this.__properties__.buffer[x][y].ch != d.ch ||
+				this.__properties__.buffer[x][y].attr != d.attr) {
+				this.__properties__.buffer[x][y] = d;
+				list.push(d);
+			}
+		}
+	}
+	this.__properties__.update={};
+	return list.sort(function(a,b) {
+		if(a.y == b.y)
+			return a.x-b.x;
+		return a.y-b.y;
+	});
+}
+Display.prototype.__getData__ = function(c,x,y) {
+	var cd = {
+		x:Number(x),
+		y:Number(y),
+		px:Number(x) + this.__properties__.x,
+		py:Number(y) + this.__properties__.y
+	};
+	if(c) {
+		var d = c.frame.getData(x-c.xoff,y-c.yoff,true);
+		cd.id = c.frame.id;
+		cd.ch = d.ch;
+		if(d.attr)
+			cd.attr = d.attr;
+		else
+			cd.attr = c.frame.attr;
+	}
+	return cd;
+}
+Display.prototype.__drawChar__ = function(ch,attr,xpos,ypos) {
+	if(attr)
+		console.attributes = attr;
+	if(xpos == console.screen_columns && ypos == console.screen_rows)
+		console.cleartoeol();
+	else if(ch == undefined) {
+		console.write(" ");
+	}
+	else {
+		console.write(ch);
+	}
+}
+Display.prototype.__getTopCanvas__ = function(x,y) {
+	var top = undefined;
+	for each(var c in this.__properties__.canvas) {
+		if(c.frame.parent == undefined || c.hasData(x,y))
+			top = c;
+	}
+	return top;
+}
+
+/* character/attribute pair representing a screen position and its contents */
+function Char(ch,attr) {
+	this.ch = ch;
+	this.attr = attr;
+}
+
+/* self-validating cursor position object */
+function Cursor(x,y,frame) {
+	this.__properties__ = {
+		x:undefined,
+		y:undefined,
+		frame:undefined
+	}
+
+	if(frame instanceof Frame)
+		this.__properties__.frame = frame;
+	else
+		throw("the frame is not a frame");
+
+	this.x = x;
+	this.y = y;
+}
+
+Cursor.prototype.__defineGetter__("x", function() {
+	return this.__properties__.x;
+});
+Cursor.prototype.__defineSetter__("x", function(x) {
+	if(isNaN(x))
+		throw("invalid x coordinate: " + x);
+	this.__properties__.x = x;
+});
+Cursor.prototype.__defineGetter__("y", function() {
+	return this.__properties__.y;
+});
+Cursor.prototype.__defineSetter__("y", function(y) {
+	if(isNaN(y))
+		throw("invalid y coordinate: " + y);
+	this.__properties__.y = y;
+});
+
+
+
+/* self-validating scroll offset object */
+function Offset(x,y,frame) {
+	this.__properties__ = {
+		x:undefined,
+		y:undefined,
+		frame:undefined
+	}
+
+	if(frame instanceof Frame)
+		this.__properties__.frame = frame;
+	else
+		throw("the frame is not a frame");
+
+	this.x = x;
+	this.y = y;
+}
+
+Offset.prototype.__defineGetter__("x", function() {
+	return this.__properties__.x;
+});
+Offset.prototype.__defineSetter__("x", function(x) {
+	if(x == undefined)
+		throw("invalid x offset: " + x);
+	else if(x < 0)
+		x = 0;
+	this.__properties__.x = x;
+});
+Offset.prototype.__defineGetter__("y", function() {
+	return this.__properties__.y;
+});
+Offset.prototype.__defineSetter__("y", function(y) {
+	if(y == undefined)
+		throw("invalid y offset: " + y);
+	else if(y < 0)
+		y = 0;
+	this.__properties__.y = y;
+});
-- 
GitLab


From a376f478fae208cc728f2b98ce5243d2327aa3c5 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 15 Sep 2020 12:57:00 -0700
Subject: [PATCH 696/752] Try to prevent Windows-git users from introduce
 carriage-returns into our revision-controlled text files. :-(

---
 .gitattributes | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/.gitattributes b/.gitattributes
index 02cec6b122..d3c138141e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,21 @@
 *.ssjs gitlab-language=javascript
+*.js   text
+*.ssjs text
+*.src  text
+*.inc  text
+*.ini  text
+*.cfg  text
+*.html text
+*.xml  text
+*.txt  text
+*.TXT  text
+*.json text
+*.bat  text
+*.mak  text
+*.mk   text
+makefile text
+Makefile text
+GNUmakefile text
+*.bin  -text
+*.cnf  -text
 
-- 
GitLab


From 5ed436a75b358d2d365d8c7bb481bb4724da4c8c Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 15 Sep 2020 16:42:59 -0700
Subject: [PATCH 697/752] Fix typo (thanks WitNik)

---
 src/sbbs3/umonitor/umonitor.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sbbs3/umonitor/umonitor.c b/src/sbbs3/umonitor/umonitor.c
index e53cd75fd1..8c5e9ef994 100644
--- a/src/sbbs3/umonitor/umonitor.c
+++ b/src/sbbs3/umonitor/umonitor.c
@@ -948,7 +948,7 @@ USAGE:
 #endif
 					         "       A = ANSI mode\n"
 					         "-l# =  set screen lines to #\n"
-					         "-s# =  set idle slsep to # milliseconds (defualt: %d)\n"
+					         "-s# =  set idle slsep to # milliseconds (default: %d)\n"
 					    ,argv[0]
 					    ,idle_sleep
 					);
-- 
GitLab


From 545bba63f903b03685b439517e786655ad0cc452 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 15 Sep 2020 17:20:27 -0700
Subject: [PATCH 698/752] Cardinal BBS rule #1: don't print in column 80.

---
 text/banner3.msg | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/text/banner3.msg b/text/banner3.msg
index db16738088..f21554bf92 100755
--- a/text/banner3.msg
+++ b/text/banner3.msg
@@ -1,2 +1,7 @@
-
-NH4B�NB�H4�NB�H4��NB�H4������NB�H��NB�H�NB�H4�0�NB�H�NB�H4�NB�W �H7�0�N H4B�NB�H��NB�H�NB�H4��NB�H�NB�H�4��0�NB�H�4߱���0���NB�H�NB�H�������NB�H4�NB�H4���NB�H4��NB�H4�NB�H4�NB�������������W H7��0�7�0�N� H7��N H���N H��N H�7۱N�H7��N H���7�0�7�N�H�7�0����N�  H7�0�7�N�H7��0��7�N � B���������������������������HW������7�N H7�0�N H�������7�0���������7�0�����N H���7��N�H����N H���N B�����������������4Kgj0B������������W H�۲N H��۲N�H�7�N �H7�0�N H��7�N H۲N H7�0ݲ�N H���7�0�7�N�H�7�N H��7�0��N H�۲N B���4Wbbs software0B���H4�NB�H4�NB�H4��NB�H4�����NB�H�NB�H��NB�H4�NB�HW�N� HB�NB�H4�NB�W �B�H4�0�NB�W H��N HB�NB�H��NB�H��NB�H���NB�H��4�NB�W H�N HB�NB�H�NB�H��4�NB�H4���������NB�H4��NB�H4�NB�Z
+N
+H4B�NB�H4�NB�H4��NB�H4������NB�H��NB�H�NB�H4�0�NB�H�NB�H4�NB�W �H7�0�N H4B�NB�H��NB�H�NB�H4��NB�H�NB�H�4��0�NB�H�4߱���0���NB�H�NB�H�������NB�H4�NB�H4���NB�H4��NB�H4�NB�
+�������������W H7��0�7�0�N� H7��N H���N H��N H�7۱N�H7��N H���7�0�7�N�H�7�0����N�  H7�0�7�N�H7��0��7�N � B�������������
+�������������HW������7�N H7�0�N H�������7�0���������7�0�����N H���7��N�H����N H���N B����������������
+4Kgj0B������������W H�۲N H��۲N�H�7�N �H7�0�N H��7�N H۲N H7�0ݲ�N H���7�0�7�N�H�7�N H��7�0��N H�۲N B���4Wbbs software0B�
+�H4�NB�H4�NB�H4��NB�H4�����NB�H�NB�H��NB�H4�NB�HW�N� HB�NB�H4�NB�W �B�H4�0�NB�W H��N HB�NB�H��NB�H��NB�H���NB�H��4�NB�W H�N HB�NB�H�NB�H��4�NB�H4���������NB�H4��NB�H4�
+Z
\ No newline at end of file
-- 
GitLab


From 1670393a28d888a56b0f1b9aca908257647f1db9 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 15 Sep 2020 22:18:28 -0700
Subject: [PATCH 699/752] 2010 redist package should no longer be needed.

---
 docs/install.rtf | 218 +++++++++++++++++++++++------------------------
 1 file changed, 106 insertions(+), 112 deletions(-)

diff --git a/docs/install.rtf b/docs/install.rtf
index 69e8411d02..0c48b43f13 100644
--- a/docs/install.rtf
+++ b/docs/install.rtf
@@ -1,4 +1,4 @@
-{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff1\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}
+{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff1\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}
 {\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}
 {\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
 {\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
@@ -9,43 +9,44 @@
 {\f55\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f57\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f58\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f59\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}
 {\f60\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f61\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f62\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f64\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}
 {\f65\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f67\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f68\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f69\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}
-{\f70\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f71\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f72\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
-{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
-{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
-{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
-{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
-{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
-{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}
-{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}
-{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
-{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
-{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
-{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
-{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
-{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
-{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
-{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
-{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}
-{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
-{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}
-{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
-{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
-{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}
-{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;
-\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;\caccentone\ctint255\cshade191\red47\green84\blue150;}{\*\defchp 
-\fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 
-\rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\s1\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel0\rin0\lin0\itap0 
-\rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink15 \sqformat heading 1;}{
-\s2\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink16 \sqformat 
-heading 2;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
+{\f70\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f71\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f72\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f384\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}
+{\f385\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f387\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f388\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f391\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
+{\f392\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
+{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
+{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}
+{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}
+{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}
+{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
+{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
+{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
+{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
+{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
+{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}
+{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}
+{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}
+{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;
+\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;
+\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;\caccentone\ctint255\cshade191\red47\green84\blue150;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 
+\fs24\lang1033\langfe1033\loch\f1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\s1\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel0\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 
+\fs24\lang1033\langfe1033\loch\f1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink15 \sqformat heading 1;}{\s2\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 
+\ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink16 \sqformat heading 2;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
 \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
-\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused 
-Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \af31503\afs32 \ltrch\fcs0 \fs32\cf19\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \af31503\afs26 \ltrch\fcs0 
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused 
+Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \fs32\cf19\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \af0\afs26 \ltrch\fcs0 
 \fs26\cf19\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink2 \slocked \ssemihidden \spriority9 Heading 2 Char;}}{\*\listtable{\list\listtemplateid-1372192284\listsimple{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0
 \levelstartat0\levelspace0\levelindent0{\leveltext\'01*;}{\levelnumbers;}}{\listname ;}\listid-2}}{\*\listoverridetable{\listoverride\listid-2\listoverridecount1{\lfolevel\listoverrideformat{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0
-\levelfollow0\levelstartat0\levelold\levelspace0\levelindent360{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 }}\ls1}}{\*\rsidtbl \rsid5988623\rsid6060643}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1
-\mwrapIndent1440\mintLim0\mnaryLim0}{\info{\operator Rob Swindell}{\creatim\yr2020\mo9\dy6\hr14\min34}{\revtim\yr2020\mo9\dy6\hr14\min42}{\version2}{\edmins8}{\nofpages2}{\nofwords527}{\nofchars3009}{\nofcharsws3529}{\vern5}}{\*\xmlnstbl {\xmlns1 http://s
-chemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect 
+\levelfollow0\levelstartat0\levelold\levelspace0\levelindent360{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 }}\ls1}}{\*\rsidtbl \rsid4523141\rsid5988623\rsid6060643}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0
+\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim0}{\info{\operator Rob Swindell}{\creatim\yr2020\mo9\dy6\hr14\min34}{\revtim\yr2020\mo9\dy15\min27}{\version3}{\edmins11}{\nofpages2}{\nofwords491}{\nofchars2803}{\nofcharsws3288}{\vern5}}
+{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect 
 \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701
 \dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot5988623 \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1
 \pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5
@@ -57,45 +58,49 @@ chemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440
 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 This version is expected to be compatible with the following Microsoft}{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \u210\'d2}{\rtlch\fcs1 \af1\afs20 
 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  operating systems (both 32-bit/x86 and 64-bit/x64 flavors, though 32-bit is preferred for 16-bit DOS program compatibility):
 \par 
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
 \faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows XP SP3 or later
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af1\dbch\af31505\loch\f1 Windows Server 2003 SP3 or later or R2
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af1\dbch\af31505\loch\f1 Windows Server 2008 SP2 or later or R2
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af1\dbch\af31505\loch\f1 Windows Vista SP2 \hich\af1\dbch\af31505\loch\f1 or later 
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af1\dbch\af31505\loch\f1 Windows 7
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af1\dbch\af31505\loch\f1 Windows 8
-\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af1\dbch\af31505\loch\f1 Windows 10
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows Server 2003 SP3 or later or R2
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows Server 2008 SP2 or later or R2
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows Vista SP2 \hich\af1\dbch\af31505\loch\f1 or later 
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows 7
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows 8
+\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3\pnindent360 {\pntxtb \'b7}}
+\faauto\ls1\rin0\lin720\itap0\pararsid5988623 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows 10
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
 \rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Prerequisites\line }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 This version of Synchronet }{\rtlch\fcs1 
-\ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 requires}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
- the Microsoft Visual C++ 2010 run-time libraries MSVCR100.DLL and VCRUNTIME140.DLL. If you don\hich\f1 \rquote \loch\f1 t have these files already installed on your system, you can dow\hich\af1\dbch\af31505\loch\f1 nload the installers from Microsoft
-\line here: }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://www.microsoft.com/en-us/download/details.aspx?id=5555 }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 
-{\*\datafield 
-00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9200000068007400740070003a002f002f007700770077002e006d006900630072006f0073006f00660074002e0063006f006d002f0065006e002d00750073002f0064006f0077006e006c006f00610064002f006400
-65007400610069006c0073002e0061007300700078003f00690064003d0035003500350035000000795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
-http://www.microsoft.com/en-us/download/details.aspx?id=5555}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \line \hich\af1\dbch\af31505\loch\f1 and here: }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 
-\ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://www.microsoft.com/en-us/download/details.aspx?id=52\hich\af1\dbch\af31505\loch\f1 685 }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
-00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b96000000680074007400700073003a002f002f007700770077002e006d006900630072006f0073006f00660074002e0063006f006d002f0065006e002d00750073002f0064006f0077006e006c006f00610064002f00
-640065007400610069006c0073002e0061007300700078003f00690064003d00350032003600380035000000795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
-https://www.microsoft.com/en-us/download/details.aspx?id=52685}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs28 \ltrch\fcs0 \fs28\insrsid6060643 
+\ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 requires}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  the Microsoft Visual C++ 201}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
+\fs20\insrsid4523141 \hich\af1\dbch\af31505\loch\f1 5}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid4523141 \hich\af1\dbch\af31505\loch\f1 C }{\rtlch\fcs1 
+\af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 run-time librar}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid4523141 \hich\af1\dbch\af31505\loch\f1 y}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
+\hich\af1\dbch\af31505\loch\f1  \hich\af1\dbch\af31505\loch\f1 VCRUNTIME140.DLL. If you }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid4523141 \hich\af1\dbch\af31505\loch\f1 do not}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
+\hich\af1\dbch\af31505\loch\f1  have th}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid4523141 \hich\af1\dbch\af31505\loch\f1 is}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  file\hich\af1\dbch\af31505\loch\f1 
+ already installed on your system, you can dow\hich\af1\dbch\af31505\loch\f1 nload the installer\hich\af1\dbch\af31505\loch\f1  from Microsoft}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid4523141 \hich\af1\dbch\af31505\loch\f1  }{\rtlch\fcs1 
+\af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 here: }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid4523141\charrsid4523141 \hich\af1\dbch\af31505\loch\f1 https://www.microsoft.com/en-us/download/details.aspx?id=53840}{
+\rtlch\fcs1 \af1\afs28 \ltrch\fcs0 \fs28\insrsid6060643 
 \par }{\rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Upgrading
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 This is }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 not an upgrade}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
 \hich\af1\dbch\af31505\loch\f1  package. If you already have a version of }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
 \hich\af1\dbch\af31505\loch\f1  installed, you }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 should not}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
- use this installation package to attempt an upgrade. Separate archives are\hich\af1\dbch\af31505\loch\f1  available for upgrading }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet}{\rtlch\fcs1 
-\af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  from previous versions at }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://www.synchro.net }{
-\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
-00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4800000068007400740070003a002f002f007700770077002e00730079006e006300680072006f002e006e00650074002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {
+ use this installation package to attempt an upgrade. \hich\af1\dbch\af31505\loch\f1 S\hich\af1\dbch\af31505\loch\f1 eparate \hich\af1\dbch\af31505\loch\f1 archive\hich\af1\dbch\af31505\loch\f1 s\hich\af1\dbch\af31505\loch\f1  
+\hich\af1\dbch\af31505\loch\f1 are\hich\af1\dbch\af31505\loch\f1  available for upgrading }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
+\hich\af1\dbch\af31505\loch\f1  from previous versions at }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://www.synchro.net }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
+\fs20\insrsid5988623 {\*\datafield 
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4800000068007400740070003a002f002f007700770077002e00730079006e006300680072006f002e006e00650074002f000000795881f43b1d7f48af2c825dc485276300000000a5ab000300}}}{\fldrslt {
 \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://www.synchro.net}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 .
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
 \rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Installation Directory
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 It is recommended that you install }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
 \hich\af1\dbch\af31505\loch\f1  into the default installation directory (}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \hich\af2\dbch\af31505\loch\f2 C:\\SBBS}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
-\hich\af1\dbch\af31505\loch\f1 ). If you choose \hich\af1\dbch\af31505\loch\f1 to install into a different location, it is }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 strongly recommended}{\rtlch\fcs1 
-\af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  that you use }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \\\hich\af2\dbch\af31505\loch\f2 SBBS}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
-\hich\af1\dbch\af31505\loch\f1  or another MS-DOS compatible 8.3 format path name (e.g. }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \hich\af2\dbch\af31505\loch\f2 D:\\SBBS}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
+\hich\af1\dbch\af31505\loch\f1 ). If you choose to install into a different location, it is }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 strongly recommended}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  that you use }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \\\hich\af2\dbch\af31505\loch\f2 SBBS}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
+ or \hich\af1\dbch\af31505\loch\f1 another MS-DOS compatible 8.3 format path name (e.g. }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \hich\af2\dbch\af31505\loch\f2 D:\\SBBS}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
 \hich\af1\dbch\af31505\loch\f1 , and }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 NOT}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  }{\rtlch\fcs1 \af2\afs20 
 \ltrch\fcs0 \f2\fs20\insrsid6060643 \hich\af2\dbch\af31505\loch\f2 D:\\SYNCHRONET}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 ).
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
@@ -103,56 +108,56 @@ https://www.microsoft.com/en-us/download/details.aspx?id=52685}}}\sectd \ltrsect
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 The installation program will create a shortcut to }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid5988623 \hich\af2\dbch\af31505\loch\f2 sbbsctrl.exe}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  on your desktop and in your startup folder (so }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  will start automatically during boot-up). See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  will start automaticall\hich\af1\dbch\af31505\loch\f1 y during boot-up). See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
 HYPERLINK http://wiki.synchro.net/monitor:sbbsctrl }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6a00000068007400740070003a002f002f00770069006b0069002e00730079006e006300680072006f002e006e00650074002f006d006f006e00690074006f0072003a0073006200620073006300740072006c000000
-795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/monitor:sbbsctrl}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 
+795881f43b1d7f48af2c825dc485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/monitor:sbbsctrl}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 
 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  for more details.
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
-\rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet Windows Services
+\rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchrone\hich\af1\dbch\af31505\loch\f1 t Windows Services
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 This \hich\af1\dbch\af31505\loch\f1 version of Synchronet includes }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \hich\af2\dbch\af31505\loch\f2 sbbsNTsvcs.exe}{\rtlch\fcs1 \af1\afs20 
-\ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  which may be used to run Synchronet as a set of operating system services on }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows}{
-\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 . The }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet Control Panel}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  can still be used to view real-time logs and statistics, monitor and control nodes, and start a\hich\af1\dbch\af31505\loch\f1 
-nd stop services, but it is not necessary for the BBS to be running and servicing clients. The }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet Control Panel}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 \hich\f1  must be run \'93\loch\f1 \hich\f1 as administrator\'94\hich\af1\dbch\af31505\loch\f1  to }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 \hich\af1\dbch\af31505\loch\f1 
-effectively communicate}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  with the Windows Service Control Manager. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
-\hich\af1\dbch\af31505\loch\f1 HYPERLINK http://wiki.synchro.net/monitor:ntsvcs }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 This version of Synchronet includes }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6060643 \hich\af2\dbch\af31505\loch\f2 sbbsNTsvcs.exe}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
+\hich\af1\dbch\af31505\loch\f1  which may be used to run Synchronet as a set of operating system services on }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Windows}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 . The }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet Control Panel}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
+\hich\af1\dbch\af31505\loch\f1  can still be used to view real-time logs and statistics, monitor and co\hich\af1\dbch\af31505\loch\f1 ntrol nodes, and start and stop services, but it is not necessary for the BBS to be running and servicing clients. The }{
+\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 \i\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet Control Panel}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 \hich\f1  must be run \'93\loch\f1 \hich\f1 
+as administrator\'94\loch\f1  to }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 \hich\af1\dbch\af31505\loch\f1 effectively communicate}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
+ with the Windows Service Control Manager. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLIN\hich\af1\dbch\af31505\loch\f1 K http://wiki.synchro.net/monitor:ntsvcs }{\rtlch\fcs1 
+\af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6600000068007400740070003a002f002f00770069006b0069002e00730079006e006300680072006f002e006e00650074002f006d006f006e00690074006f0072003a006e00740073007600630073000000795881f4
-3b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/monitor:ntsvcs}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 
+3b1d7f48af2c825dc485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/monitor:ntsvcs}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 
 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  for more details.
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
 \rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Documentation
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 The majority of the sysop documentation (}{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://sync\hich\af1\dbch\af31505\loch\f1 
-hro.net/docs/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
-00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4a00000068007400740070003a002f002f00730079006e006300680072006f002e006e00650074002f0064006f00630073002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 The maj\hich\af1\dbch\af31505\loch\f1 ority of the sysop documentation (}{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
+HYPERLINK http://synchro.net/docs/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4a00000068007400740070003a002f002f00730079006e006300680072006f002e006e00650074002f0064006f00630073002f000000795881f43b1d7f48af2c825dc485276300000000a5ab000318}}}{\fldrslt {
 \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://synchro.net/docs/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
-) is in the process of being converted to Wiki format (}{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://wiki.synchro.net/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid5988623 {\*\datafield 
-00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4a00000068007400740070003a002f002f00770069006b0069002e00730079006e006300680072006f002e006e00650074002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {
+) is in the process of being converted to Wiki format (}{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK\hich\af1\dbch\af31505\loch\f1  http://wiki.synchro.net/ }{\rtlch\fcs1 \af1\afs20 
+\ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4a00000068007400740070003a002f002f00770069006b0069002e00730079006e006300680072006f002e006e00650074002f000000795881f43b1d7f48af2c825dc485276300000000a5ab000300}}}{\fldrslt {
 \rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
 ) and all new documentation will be created on the wiki.
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
 \rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Licensing/Registration/Donations
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet is free software covered under the GNU General Public License. No registration or licensing fee is required. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
-\hich\af1\dbch\af31505\loch\f1 HYPERLINK http://synchro.net/copyright.html }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet is free\hich\af1\dbch\af31505\loch\f1  software covered under the GNU General Public License. No registration or licensing fee is required. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 
+\ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://synchro.net/copyright.html }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5c00000068007400740070003a002f002f00730079006e006300680072006f002e006e00650074002f0063006f0070007900720069006700680074002e00680074006d006c000000795881f43b1d7f48af2c825dc485
-276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://synchro.net/copyright.html}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  for details.
+276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 ht\hich\af1\dbch\af31505\loch\f1 tp://synchro.net/copyright.html}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 
+\af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  for details.
 \par 
-\par \hich\af1\dbch\af31505\loch\f1 The \hich\af1\dbch\af31505\loch\f1 author/maintainer (}{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 rob@synchro.net}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
-\hich\af1\dbch\af31505\loch\f1 ) has a PayPal}{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \u210\'d2}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
- account for accepting voluntary monetary }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 donations}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 .
+\par \hich\af1\dbch\af31505\loch\f1 The author/maintainer (}{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 rob@synchro.net}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
+) has a PayPal}{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid6060643 \loch\af3\dbch\af31505\hich\f3 \u210\'d2}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  account for accepting voluntary monetary }{
+\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 donations}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 .
 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\keepn\widctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {
 \rtlch\fcs1 \ab\ai\af1\afs28 \ltrch\fcs0 \b\i\fs28\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Source Code
 \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af1\hich\af1\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
-\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchronet is an open source development project with complete anonymous public access to the full C/C++ source code. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 
-\hich\af1\dbch\af31505\loch\f1 HYPERLINK http://wik\hich\af1\dbch\af31505\loch\f1 i.synchro.net/dev:index }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
+\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Synchr\hich\af1\dbch\af31505\loch\f1 onet is an open source development project with complete anonymous public access to the full C/C++ source code. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 
+\ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://wiki.synchro.net/dev:index }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5c00000068007400740070003a002f002f00770069006b0069002e00730079006e006300680072006f002e006e00650074002f006400650076003a0069006e006400650078000000795881f43b1d7f48af2c825dc485
-276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/dev:index}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
+276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/dev:index}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 
 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  for details.
 \par 
 \par }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Thank you}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1  for your interest in }{\rtlch\fcs1 \ai\af1\afs20 \ltrch\fcs0 
@@ -161,7 +166,7 @@ hro.net/docs/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\data
 \par }{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 Rob Swindell, }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 
 HYPERLINK http://wiki.synchro.net/person:digital_man }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid5988623 {\*\datafield 
 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6e00000068007400740070003a002f002f00770069006b0069002e00730079006e006300680072006f002e006e00650074002f0070006500720073006f006e003a006400690067006900740061006c005f006d006100
-6e000000795881f43b1d7f48af2c825dc485276300000000a5ab0003}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/person:digital_man}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {
+6e000000795881f43b1d7f48af2c825dc485276300000000a5ab00034e}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \fs20\insrsid6060643 \hich\af1\dbch\af31505\loch\f1 http://wiki.synchro.net/person:digital_man}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {
 \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\fs20\insrsid6060643 
 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
@@ -246,27 +251,16 @@ faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca
 \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);
 \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;
 \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;
-\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;
-\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;
-\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;
-\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;
-\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;
-\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;
-\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;
-\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;
+\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
+\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
+\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
+\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
+\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
 \lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
@@ -317,8 +311,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff
 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000f0e9
-0da29684d601feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
+ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000904e
+f0a0318bd601feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
 0000000000000000000000000000000000000000000000000105000000000000}}
\ No newline at end of file
-- 
GitLab


From a0d46cded9286299f265ac993bf109b6b417dc32 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Tue, 15 Sep 2020 22:19:24 -0700
Subject: [PATCH 700/752] Resolve race condition around temp directory creation
 It's possible that isdir(temp_dir) will be false and then a mkpath() call
 will fail because some other thread came in and crated the directory at that
 very moment - so save errno if mkdir fails and call isdir() again to
 double-check that the directory wasn't created by someone else already.

---
 src/sbbs3/load_cfg.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/load_cfg.c b/src/sbbs3/load_cfg.c
index 61c69d880b..ee0ecb55f8 100644
--- a/src/sbbs3/load_cfg.c
+++ b/src/sbbs3/load_cfg.c
@@ -347,8 +347,11 @@ int md(const char* inpath)
 		*p = '\0';
 
 	if(!isdir(path)) {
-		if(mkpath(path) != 0)
-			return errno;
+		if(mkpath(path) != 0) {
+			int result = errno;
+			if(!isdir(path)) // race condition: did another thread make the directory already?
+				return result;
+		}
 	}
 	
 	return 0;
-- 
GitLab


From e92272c0593db1da55d422ec2cc26173dc232760 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 13:24:16 -0700
Subject: [PATCH 701/752] modopts.ini setting to disable new-user survey
 question

Set survey=false in the [newuser] section of modopts.ini to disable the
"Where did you hear about this BBS?" new user question.
---
 exec/newuser.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/newuser.js b/exec/newuser.js
index 95ef1ca915..bde4e98da1 100644
--- a/exec/newuser.js
+++ b/exec/newuser.js
@@ -28,7 +28,7 @@ if(options.send_newuser_welcome)	// backwards compatibility hack
 
 console.clear();
 
-if(!user.address.length && user.number>1) {
+if(!user.address.length && user.number>1 && options.survey !== false) {
 	print("\1y\1hWhere did you hear about this BBS?");
 	user.address=console.getstr(30,K_LINE);
 }
-- 
GitLab


From 3db247ba04ff32af94765ca3443800c47593d369 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 20:01:48 -0700
Subject: [PATCH 702/752] Fix "jsexec lord.js +IGM grabbag/grabbag"
 installation

---
 xtrn/lord/grabbag/grabbag.js | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/xtrn/lord/grabbag/grabbag.js b/xtrn/lord/grabbag/grabbag.js
index 786052fc26..0b81a7f4d2 100644
--- a/xtrn/lord/grabbag/grabbag.js
+++ b/xtrn/lord/grabbag/grabbag.js
@@ -3014,24 +3014,6 @@ function run_maint(b)
 var bs;
 load('array.js'); // probably no longer needed
 var mnum = undefined;
-if (player.level === 1)
-{
-	mnum = random(10);
-}
-else
-{
-	if (random(6) !== 2)
-	{
-		mnum = ((player.level-1)*11)+random(10);
-	}
-	else
-	{
-		mnum = (random(player.level) * 11) + random(10);
-	}
-}
-
-var husband = load_monster(mnum); // sets up monster battle and nymph husband
-
 // end globals
 
 function main()
@@ -3185,6 +3167,24 @@ if (argc == 1 && argv[0] == 'INSTALL') {
 	exit(0);
 }
 else {
+	if (player.level === 1)
+	{
+		mnum = random(10);
+	}
+	else
+	{
+		if (random(6) !== 2)
+		{
+			mnum = ((player.level-1)*11)+random(10);
+		}
+		else
+		{
+			mnum = (random(player.level) * 11) + random(10);
+		}
+	}
+
+	var husband = load_monster(mnum); // sets up monster battle and nymph husband
+
 	main();
 	exit(0); // most routines end with good_bye();
 }
-- 
GitLab


From b732f9b68113b17ccf46d9d520de562ee3808738 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 20:10:58 -0700
Subject: [PATCH 703/752] Include "Main" an install target category.

---
 xtrn/hamtest/install-xtrn.ini | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/xtrn/hamtest/install-xtrn.ini b/xtrn/hamtest/install-xtrn.ini
index a18af1724b..d72c0ec2f9 100644
--- a/xtrn/hamtest/install-xtrn.ini
+++ b/xtrn/hamtest/install-xtrn.ini
@@ -1,9 +1,9 @@
 Name: HAM Test
 Desc: Amateur radio operator license exam practice tests
 By:   Deuce (Stephen Hurd)
-Cats: Tests
+Cats: Tests, Main
 Subs: Radio, HAM, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/17 08:27:36 rswindell Exp $
+Inst: 2020/09/16
 
 [prog:HAMTEST]
 cmd  = ?hamtest.js
-- 
GitLab


From 7443efb1e69ef38c108e8ab19c9ccd0afdf60dd0 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 20:12:05 -0700
Subject: [PATCH 704/752] Include option to share game data with romulusbbs.com

Why isn't this fat cats bbs dot com? <shrugs>
---
 xtrn/druglord/install-xtrn.ini | 8 +++++++-
 xtrn/druglord/server.ini       | 2 ++
 xtrn/fatfish/install-xtrn.ini  | 8 +++++++-
 xtrn/fatfish/server.ini        | 2 ++
 4 files changed, 18 insertions(+), 2 deletions(-)
 create mode 100644 xtrn/druglord/server.ini
 create mode 100644 xtrn/fatfish/server.ini

diff --git a/xtrn/druglord/install-xtrn.ini b/xtrn/druglord/install-xtrn.ini
index 1e648a5f1c..4bbaac2a7e 100644
--- a/xtrn/druglord/install-xtrn.ini
+++ b/xtrn/druglord/install-xtrn.ini
@@ -3,7 +3,7 @@ Desc: Similar to Dope Wars and its derivatives
 By:   art, at fatcatsbbs dot com
 Cats: Games
 Subs: Drugs, Simulation, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 09:13:48 rswindell Exp $
+Inst: 2020/09/16
 
 [prog:DRUGLORD]
 cmd  = ?druglord.js
@@ -11,6 +11,12 @@ settings = XTRN_MULTIUSER
 execution_ars = ANSI
 required = true
 
+[ini:server.ini]
+prompt = Share data with romulusbbs.com
+keys = host
+values = 'romulusbbs.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:druglord]
diff --git a/xtrn/druglord/server.ini b/xtrn/druglord/server.ini
new file mode 100644
index 0000000000..aa1e15cde4
--- /dev/null
+++ b/xtrn/druglord/server.ini
@@ -0,0 +1,2 @@
+host=localhost
+port=10088
diff --git a/xtrn/fatfish/install-xtrn.ini b/xtrn/fatfish/install-xtrn.ini
index 0d8b2eb729..41b0ab6101 100644
--- a/xtrn/fatfish/install-xtrn.ini
+++ b/xtrn/fatfish/install-xtrn.ini
@@ -3,7 +3,7 @@ Desc: Fishing simulation door game
 By:   art, at fatcatsbbs dot com
 Cats: Games
 Subs: Sport, Simulation, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 08:05:27 rswindell Exp $
+Inst: 2020/09/16
 
 [prog:FATFISH]
 cmd  = ?fatfish.js
@@ -11,6 +11,12 @@ settings = XTRN_MULTIUSER
 execution_ars = ANSI
 required = true
 
+[ini:server.ini]
+prompt = Share data with romulusbbs.com
+keys = host
+values = 'romulusbbs.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:fatfish]
diff --git a/xtrn/fatfish/server.ini b/xtrn/fatfish/server.ini
new file mode 100644
index 0000000000..aa1e15cde4
--- /dev/null
+++ b/xtrn/fatfish/server.ini
@@ -0,0 +1,2 @@
+host=localhost
+port=10088
-- 
GitLab


From c9c0a8680f8c3535b4de7e90f3e71bb47f6eb4c1 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 20:13:47 -0700
Subject: [PATCH 705/752] List of new features in v3.18

---
 docs/v318_new.txt | 107 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 docs/v318_new.txt

diff --git a/docs/v318_new.txt b/docs/v318_new.txt
new file mode 100644
index 0000000000..611e5476cb
--- /dev/null
+++ b/docs/v318_new.txt
@@ -0,0 +1,107 @@
+***************************************************
+* What's New in Synchronet Version 3.18 for Win32 *
+* (ChangeLog since v3.17b for Win32 Jan 01, 2019) *
+***************************************************
+
+Terminal Server
+~~~~~~~~~~~~~~~
+o Server-side JavaScript for dynamically-loaded BBS modules 
+  and dynamic-HTML indexes for FTP server
+o UTF-8 support
+  - Auto-detect UTF-8 terminals
+  - Automatic translation of DOS/native door game output
+  - Auto-translation between ASCII/CP437, PETSCII, and UTF-8
+  - Create and read/translate UTF-8 encoded networked messages, email
+  - UNICODE display tricks (e.g. double-wide chars, special chars)
+o New ARS keywords: UTF8, CP437, ASCII
+o Improved CBM/PETSCII terminal support
+o Improved support for 40-column terminals
+o Improved support for terminals > 80 columns in width
+o Bright-background (PETSCII and ANSI iCE-color) terminals supported
+o Mouse-reporting terminal support (click-able menu options, scroll-wheel)
+o Fast-logon support (logging-in as "!user-id"), enabled via modopts.ini
+o Automatic detection of Raw TCP client connections on Telnet ports
+o Automatic text-portion of multi-part MIME-messages in QWK message packets
+o New operator option to delete numeric ranges of messages in sub-boards
+o Configurable treatment of "Soft-CRs" created by external message editors
+o @-codes:
+  - Many added (including some additional PCBoard codes)
+  - New output formatting options
+o New helpful sysop debugging command: ECHO and EVAL
+o Auto-ZMODEM upload of message text from user
+ (internal editor and fseditor.js)
+o sexyz:
+  - Improved YMODEM-G file transfer reliability / compatibility
+  - Improved ZMODEM downloads where the receiver already has the file
+o Built-in support for terminal output rate limiting
+  (e.g. 9600bps simulation) - requires compatible terminal
+o Support the fast display of giant (e.g. multi-gigabyte) text files
+o Arrow-up/down history scroll:
+  - sysop command arguments
+  - netmail recipient addresses
+o Sunrise (DoorFrame) multinode door setup convenience environment variables:
+  PCBDIR and PCBDRIVE
+o ftn-setup.js: menu-driven setup of several FidoNet-style networks
+o xtrn-setup.js: menu-driven setup of many external programs (e.g. door games)
+o Customizable spinning cursors (10 of them!) in the text.dat
+o Easy *random* display file/menu selection (just use wildcards!)
+o more loadable modules: automsg, list msgs, text_sec, logonlist
+o Message base load/read/scan optimizations
+o User notifications of messages posted to them via QWKnet
+
+General
+~~~~~~~
+o install-xtrn.js: automated/wizard-like install of external programs
+  - run 'jsexec -auto /sbbs/xtrn/*' to auto-discover/install new xtrn progs
+o Support for C-style escaping in trashcan/filter files
+  (e.g. "\ " or "\x20" for a leading space character)
+o Sysop email notifications upon chat-paging and logged-errors
+o user presence and private messages shared between webv4 and terminal users
+o Windows build is now linked with dynamic C/C++ runtime libraries
+o Ctrl-F (find) and Ctrl-G (repeat find) in SCFG and EchoCfg
+o exportcfg.js: export SBBS configuration data to various file formats
+o qnet-ftp.js: new QWKnet/FTP module
+o FTP server file listing optimizations
+o Numerous mail server improvements
+  (e.g. msg priority, MIME-encoded header fields)
+
+Security
+~~~~~~~~
+o User security notifications: failed login attempts, password requests
+o User password maximum length increased from 8 to 40 characters
+o Sysop-reset of the failed-login/temp-ban list via "ctrl/clear" sem file
+o Updated TLS/SSL/SSH library (Cryptlib 3.4.5) and patched for compatibility
+  and security
+o Extended user credits and upload/download byte-stats from 2GB to 4GB
+  
+JavaScript
+~~~~~~~~~~
+o JSexec default behavior can be configured via ctrl/jsexec.ini
+o IPv6 socket support
+o New methods: file_mode(), file_chmod(), utf8_encode(), utf8_decode(),
+  utf8_get_width(), str_is_utf8(), str_is_utf16(), str_is_ascii(),
+  str_has_ctrl(), js.exec(), system.get_node(), bbs.show_msg(),
+  bbs.show_msg_header(), bbs.download_msg_attachments(),
+  bbs.change_msg_attr(), console.wide(), console.output_rate,
+  MsgBase.get_index(), MsgBase.dump_msg_header(), system.notify(),
+  console.add_hotspot(), console.clear_hotspots(), console.scroll_hotspots(),
+  user.downloaded_file()
+o New properties: system.min_password_length, system.max_password_length,
+  system.autodel, client.user_name, bbs.msghdr_top_of_screen, console.type,
+  console.charset, console.mouse_mode, console.current_row, 
+  console.keyboard_buffer_level, console.keyboard_buffer_space, Queue.orphan,
+  msg_area.grp[]/grp_list[].can_access, msg_area.sub[].can_access,
+  file_area.lib[]/lib_list[].can_access, file_area.dir[].can_access
+
+Control Panel
+~~~~~~~~~~~~~
+o FidoNet related menu items
+o File->Run menu options to auto-install FidoNet networks, external programs
+o Now defaults to "dark mode"
+  (File->Import Settings->sbbsctrl.darkmode.ini to choose it)
+
+Web Server
+~~~~~~~~~~
+o More RFC/standards-conformant HTTP[S] support
+o HSTS support: https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
+o Manyfold send-file through-put performance improvement
-- 
GitLab


From 29545515bdb4f840ef7d348bcf7921a038d657a4 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 20:26:46 -0700
Subject: [PATCH 706/752] Another game that can share data (with
 romulusbbs.com)

---
 xtrn/synkroban/install-xtrn.ini | 8 +++++++-
 xtrn/synkroban/server.ini       | 2 ++
 2 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 xtrn/synkroban/server.ini

diff --git a/xtrn/synkroban/install-xtrn.ini b/xtrn/synkroban/install-xtrn.ini
index 88b7c8090d..dedb5f3e27 100644
--- a/xtrn/synkroban/install-xtrn.ini
+++ b/xtrn/synkroban/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: Sokoban warehouse game, where you move the boxes onto the goals.
 By:   art, at fatcatsbbs dot com
 Cats: Games
 Subs: Warehouse, Simulation, Multiplayer, JavaScript
-Inst: $Id: install-xtrn.ini,v 1.1 2020/04/17 09:17:37 rswindell Exp $
+Inst: 2020/09/16
 
 [prog:SOKOBAN]
 cmd  = ?synkroban.js
 settings = XTRN_MULTIUSER
 required = true
 
+[ini:server.ini]
+prompt = Share data with romulusbbs.com
+keys = host
+values = 'romulusbbs.com'
+done = true
+
 !include install-json-service.ini
 
 [ini:json-service.ini:synkroban]
diff --git a/xtrn/synkroban/server.ini b/xtrn/synkroban/server.ini
new file mode 100644
index 0000000000..aa1e15cde4
--- /dev/null
+++ b/xtrn/synkroban/server.ini
@@ -0,0 +1,2 @@
+host=localhost
+port=10088
-- 
GitLab


From 9bd2dd1a8e3815c4495c8ca78916421db42a8e6e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Wed, 16 Sep 2020 20:37:05 -0700
Subject: [PATCH 707/752] Log message change: "Raw TCP" instead of "Raw/TCP"
 oooh.... ahhhh...

---
 src/sbbs3/answer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sbbs3/answer.cpp b/src/sbbs3/answer.cpp
index a889f5d839..38f3471164 100644
--- a/src/sbbs3/answer.cpp
+++ b/src/sbbs3/answer.cpp
@@ -452,7 +452,7 @@ bool sbbs_t::answer()
 			if(telnet_rows >= TERM_ROWS_MIN && telnet_rows <= TERM_ROWS_MAX)
 				rows = telnet_rows;
 		} else {
-			lprintf(LOG_NOTICE, "no Telnet commands received, reverting to Raw/TCP mode");
+			lprintf(LOG_NOTICE, "no Telnet commands received, reverting to Raw TCP mode");
 			telnet_mode |= TELNET_MODE_OFF;
 			client.protocol = "Raw";
 			client_on(client_socket, &client,/* update: */true);
-- 
GitLab


From dd43fc6c24ee14ea0f507c8e0ace6a4c5a1b1c5a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 17 Sep 2020 01:14:44 -0700
Subject: [PATCH 708/752] Add list of SBBSecho changes since the last
 Synchronet release.

---
 docs/v318_new.txt | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/docs/v318_new.txt b/docs/v318_new.txt
index 611e5476cb..05ca779db6 100644
--- a/docs/v318_new.txt
+++ b/docs/v318_new.txt
@@ -93,6 +93,28 @@ o New properties: system.min_password_length, system.max_password_length,
   msg_area.grp[]/grp_list[].can_access, msg_area.sub[].can_access,
   file_area.lib[]/lib_list[].can_access, file_area.dir[].can_access
 
+SBBSecho
+~~~~~~~~
+o Import performance improvements (new smblib, twitlist caching)
+o Configurable Soft-CR (0x8D) import behavior: default is to leave-alone
+o AreaFix support for links with 5D addresses (zone:net/node[.point]@domain)
+o Tic passwords up to 40 chars in length
+o Explicit CHRS and NOTE header (control paragraph) import/export support
+o Ctrl-A to '@' import-conversion for invalid control paragraphs
+o Better "Kill Sent" (KFS) support on exported mail
+o Export the original message author's editor columns (COLS kludge) when known
+o Simplified and more deterministic TZUTC (timezone) import logic
+  (just store the UTC offset, don't infer the location)
+o Ability to operate without an Area File (for systems with no downlinks)
+  (set AutoAddsSubs=true, AutoAddToAreaFile=false)
+o Auto-detection of UTF-8 encoded message body text
+  (and never strip Soft-CRs from them)
+o sbbsecho.ini format is more compatible crudini/python-iniparse
+o NetMail Robots (e.g. tickit-FileFix, a.k.a. TickFix) are now supported
+o New remote FileFix module (tickfix.js) for use with tickit.js 
+o Removed old 80-column word-wrap on import behavior
+o Include Message and Reply-IDs in export NetMail messages
+
 Control Panel
 ~~~~~~~~~~~~~
 o FidoNet related menu items
-- 
GitLab


From 5330887517b9a29c8b110801612996a7c6043342 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 13:32:29 -0700
Subject: [PATCH 709/752] Added missing/better online help on Command Shells.

---
 src/sbbs3/scfg/scfg.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/scfg/scfg.c b/src/sbbs3/scfg/scfg.c
index d34e90c431..e6ca0748c3 100644
--- a/src/sbbs3/scfg/scfg.c
+++ b/src/sbbs3/scfg/scfg.c
@@ -474,6 +474,7 @@ int main(int argc, char **argv)
 			"    Chat Features        : Chat actions, sections, pagers, and robots\n"
 			"    Message Areas        : Message area configuration\n"
 			"    Message Options      : Message and e-mail options\n"
+			"    Command Shells       : Terminal server user interface/menu modules\n"
 			"    External Programs    : Events, editors, and online programs (doors)\n"
 			"    Text File Sections   : Text file areas available for online viewing\n"
 			"\n"
@@ -1009,8 +1010,8 @@ void shell_cfg()
 			"`Command Shells:`\n"
 			"\n"
 			"This is a list of `Command Shells` configured for your system.\n"
-			"Command shells are the programmable command and menu structures which\n"
-			"are available for the users of your BBS's terminal server.\n"
+			"Command shells are modules that provide the user interface and menu\n"
+			"structure for the remote users of your BBS's terminal server.\n"
 			"\n"
 			"To add a command shell section, select the desired location with the\n"
 			"arrow keys and hit ~ INS ~.\n"
-- 
GitLab


From 6b993a0bac27db7987ec54ceefff49a6181f5a07 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 13:32:58 -0700
Subject: [PATCH 710/752] Better sysop availability (for chat)
 visibility/toggleability

- JS system.operator_available property (read/writeable)
- SYSAVAIL @-code which expands to LiSysopAvailable or LiSysopNotAvailable
  (use the new SYSAVAIL @-code in the chat menu to show availabilty to chat)
- ;avail sysop command (in str_cmds.js) to toggle sysop availability
- Changed LiSysopIs text.dat string to be a format string (include %s) and
  the trailing \r\n, so that it can be used in str_cmds.js or anywhere else
  to report sysop availability to chat, or can be set to blank string to
  display nothing (this would not work previously).
---
 ctrl/text.dat             |  2 +-
 src/sbbs3/atcodes.cpp     |  3 +++
 src/sbbs3/js_system.c     | 14 +++++++++++++-
 src/sbbs3/logon.cpp       |  3 ++-
 src/sbbs3/text_defaults.c |  2 +-
 text/menu/chat.msg        |  2 +-
 6 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/ctrl/text.dat b/ctrl/text.dat
index 0caa879c81..ae962dbe3e 100644
--- a/ctrl/text.dat
+++ b/ctrl/text.dat
@@ -423,7 +423,7 @@
 "\1bLogons Today    : \1c%-5u \1b(Max \1c%u\1b)\r\n"         352 LiLogonsToday
 "\1bTime on Today   : \1c%-5u \1b(Max \1c%u\1b)\r\n"        353 LiTimeonToday
 "\1bMail Waiting    : \1c%u\r\n"                          354 LiMailWaiting
-"\1bSysop is        : \1c"                                355 LiSysopIs
+"\1b\1hSysop is        : \1c%s\r\n"                     355 LiSysopIs
 "Available"                                             356 LiSysopAvailable
 "Not Available"                                         357 LiSysopNotAvailable
 "\1r\1h\1iYou can't possibly be in two places at the "\    358 UserOnTwoNodes
diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp
index eb1625178d..6f166b3cbd 100644
--- a/src/sbbs3/atcodes.cpp
+++ b/src/sbbs3/atcodes.cpp
@@ -456,6 +456,9 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool
 	if(!strcmp(sp,"SYSOP"))
 		return(cfg.sys_op);
 
+	if(strcmp(sp, "SYSAVAIL") == 0)
+		return text[sysop_available(&cfg) ? LiSysopAvailable : LiSysopNotAvailable];
+
 	if(!strcmp(sp,"LOCATION"))
 		return(cfg.sys_location);
 
diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c
index fe45f2c99e..937bc42942 100644
--- a/src/sbbs3/js_system.c
+++ b/src/sbbs3/js_system.c
@@ -51,6 +51,7 @@ extern JSClass js_system_class;
 enum {
 	 SYS_PROP_NAME
 	,SYS_PROP_OP
+	,SYS_PROP_OP_AVAIL
 	,SYS_PROP_ID
 	,SYS_PROP_MISC
 	,SYS_PROP_INETADDR
@@ -146,6 +147,9 @@ static JSBool js_system_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 		case SYS_PROP_OP:
 			p=cfg->sys_op;
 			break;
+		case SYS_PROP_OP_AVAIL:
+			*vp=BOOLEAN_TO_JSVAL(sysop_available(cfg));
+			break;
 		case SYS_PROP_ID:
 			p=cfg->sys_id;
 			break;
@@ -359,10 +363,16 @@ static JSBool js_system_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict
 		case SYS_PROP_MISC:
 			JS_ValueToInt32(cx, *vp, &sys->cfg->sys_misc);
 			break;
+		case SYS_PROP_OP_AVAIL:
+			if(!set_sysop_availability(sys->cfg, JSVAL_TO_BOOLEAN(*vp))) {
+				JS_ReportError(cx, "%s: Failed to set sysop availability", __FUNCTION__);
+				return JS_FALSE;
+			}
+			break;
 	}
 #endif
 
-	return(TRUE);
+	return JS_TRUE;
 }
 
 
@@ -374,6 +384,7 @@ static jsSyncPropertySpec js_system_properties[] = {
 #ifndef JSDOOR
 	{	"name",						SYS_PROP_NAME,		SYSOBJ_FLAGS,		310  },
 	{	"operator",					SYS_PROP_OP,		SYSOBJ_FLAGS,		310  },
+	{	"operator_available",		SYS_PROP_OP_AVAIL,	JSPROP_ENUMERATE,	31801  },
 	{	"qwk_id",					SYS_PROP_ID,		SYSOBJ_FLAGS,		310  },
 	{	"settings",					SYS_PROP_MISC,		JSPROP_ENUMERATE,	310  },
 	{	"inetaddr",					SYS_PROP_INETADDR,	JSPROP_READONLY,	310  },	/* alias */
@@ -452,6 +463,7 @@ static jsSyncPropertySpec js_system_properties[] = {
 static char* sys_prop_desc[] = {
 	 "BBS name"
 	,"operator name"
+	,"operator is available for chat"
 	,"system QWK-ID (for QWK packets)"
 	,"settings bitfield (see <tt>SYS_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
 	,"Internet address (host or domain name)"
diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index af92047f7b..997e309bdf 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -467,8 +467,9 @@ bool sbbs_t::logon()
 		bprintf(text[LiTimeonToday],useron.ttoday
 			,cfg.level_timeperday[useron.level]+useron.min);
 		bprintf(text[LiMailWaiting],mailw);
-		bprintf("%s%s\r\n\r\n", text[LiSysopIs]
+		bprintf(text[LiSysopIs]
 			, text[sysop_available(&cfg) ? LiSysopAvailable : LiSysopNotAvailable]);
+		newline();
 	}
 
 	if(sys_status&SS_EVENT)
diff --git a/src/sbbs3/text_defaults.c b/src/sbbs3/text_defaults.c
index 2abdb4139b..064b44f5ba 100644
--- a/src/sbbs3/text_defaults.c
+++ b/src/sbbs3/text_defaults.c
@@ -576,7 +576,7 @@ const char * const text_defaults[TOTAL_TEXT]={
 	,"\x01\x62\x54\x69\x6d\x65\x20\x6f\x6e\x20\x54\x6f\x64\x61\x79\x20\x20\x20\x3a\x20\x01\x63\x25\x2d\x35\x75\x20\x01\x62\x28\x4d\x61"
 		"\x78\x20\x01\x63\x25\x75\x01\x62\x29\x0d\x0a" // 353 LiTimeonToday
 	,"\x01\x62\x4d\x61\x69\x6c\x20\x57\x61\x69\x74\x69\x6e\x67\x20\x20\x20\x20\x3a\x20\x01\x63\x25\x75\x0d\x0a" // 354 LiMailWaiting
-	,"\x01\x62\x53\x79\x73\x6f\x70\x20\x69\x73\x20\x20\x20\x20\x20\x20\x20\x20\x3a\x20\x01\x63" // 355 LiSysopIs
+	,"\x01\x62\x01\x68\x53\x79\x73\x6f\x70\x20\x69\x73\x20\x20\x20\x20\x20\x20\x20\x20\x3a\x20\x01\x63\x25\x73\x0d\x0a" // 355 LiSysopIs
 	,"\x41\x76\x61\x69\x6c\x61\x62\x6c\x65" // 356 LiSysopAvailable
 	,"\x4e\x6f\x74\x20\x41\x76\x61\x69\x6c\x61\x62\x6c\x65" // 357 LiSysopNotAvailable
 	,"\x01\x72\x01\x68\x01\x69\x59\x6f\x75\x20\x63\x61\x6e\x27\x74\x20\x70\x6f\x73\x73\x69\x62\x6c\x79\x20\x62\x65\x20\x69\x6e\x20\x74"
diff --git a/text/menu/chat.msg b/text/menu/chat.msg
index 83cff1b18b..ec06bf006e 100644
--- a/text/menu/chat.msg
+++ b/text/menu/chat.msg
@@ -5,7 +5,7 @@
 ����4 HWJoin a Chat NB����
 � HCJY NCJoin/initiate multinode chatW�B�
 � HCPNC Join/start private node/node chatG B�
-� HCCY NCChat with @SYSOP-L##############@G B�
+� HCCY NCChat with Sysop: HY@SYSAVAIL-L####@NG B�
 � HCTY NCTalk with The Guru (AI)W�B�
 � HCFY NCFinger (query) remote user/systemW B�
 � HCRY NCInternet Relay Chat (IRC)W�B�
-- 
GitLab


From 6f74f947c16417b673af28de897bbfd7f3bdcec1 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 17 Sep 2020 15:34:20 -0700
Subject: [PATCH 711/752] Add ;avail sysop command to toggle sysop-availability
 (to chat)

---
 exec/str_cmds.js | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/exec/str_cmds.js b/exec/str_cmds.js
index ca9f02ae59..99f4edf77a 100644
--- a/exec/str_cmds.js
+++ b/exec/str_cmds.js
@@ -65,6 +65,16 @@ function str_cmds(str)
 		//sync
 
 		// ######################## SYSOP Functions ##############################
+		if(str=="HELP") {
+			writeln("AVAIL\tToggle sysop chat availability");
+		}
+		if(str=="AVAIL") {
+			system.operator_available = !system.operator_available;
+			write(format(bbs.text(text.LiSysopIs)
+				, bbs.text(system.operator_available ? text.LiSysopAvailable : text.LiSysopNotAvailable)));
+			return;
+		}
+
 		if(str=="HELP") {
 			writeln("ERR\tDisplay currrent error log and opptionally delete it as well as");
 			writeln("\toptionally clearing all nodes error counters.");
-- 
GitLab


From b2b014fc723aaba877e263d10269de3985fad3aa Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 17 Sep 2020 17:07:25 -0700
Subject: [PATCH 712/752] When chat() fails (e.g. to open a file), display an
 error dialog

rather than just falling over and twitching like an injured slug
---
 src/sbbs3/umonitor/chat.c     | 21 +++++++++++----------
 src/sbbs3/umonitor/umonitor.c | 14 ++++++++++----
 2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/sbbs3/umonitor/chat.c b/src/sbbs3/umonitor/chat.c
index 389d9319c5..3cab69648a 100644
--- a/src/sbbs3/umonitor/chat.c
+++ b/src/sbbs3/umonitor/chat.c
@@ -129,7 +129,7 @@ int chat(scfg_t *cfg, int nodenum, node_t *node, box_t *boxch, void(*timecallbac
 	int		in,out;
 	char	inpath[MAX_PATH];
 	char	outpath[MAX_PATH];
-	char	usrname[128];
+	char	usrname[128] = "Unknown user";
 	char	*p;
 	char	ch;
 	time_t	now;
@@ -139,34 +139,35 @@ int chat(scfg_t *cfg, int nodenum, node_t *node, box_t *boxch, void(*timecallbac
 
 	gettextinfo(&ti);
 	if((buf=(char *)alloca(ti.screenwidth*ti.screenheight*2))==NULL) {
-		return(-1);
+		return __LINE__;
 	}
 
 	if(getnodedat(cfg,nodenum,node,FALSE,NULL))
-		return(-1);
+		return __LINE__;
 
 	username(cfg,node->useron,usrname);
 
-	gettext(1,1,ti.screenwidth,ti.screenheight,buf);
-	drawchatwin(boxch,usrname,cfg->sys_op);
-
 	sprintf(outpath,"%slchat.dab",cfg->node_path[nodenum-1]);
 	if((out=sopen(outpath,O_RDWR|O_CREAT|O_BINARY,O_DENYNONE
 		,DEFFILEMODE))==-1)
-		return(-1);
+		return __LINE__;
 
 	sprintf(inpath,"%schat.dab",cfg->node_path[nodenum-1]);
 	if((in=sopen(inpath,O_RDWR|O_CREAT|O_BINARY,O_DENYNONE
 		,DEFFILEMODE))==-1) {
 		close(out);
-		return(-1);
+		return __LINE__;
     }
 
 	if((p=(char *)alloca(PCHAT_LEN))==NULL) {
 		close(in);
 		close(out);
-		return(-1);
+		return __LINE__;
     }
+
+	gettext(1,1,ti.screenwidth,ti.screenheight,buf);
+	drawchatwin(boxch,usrname,cfg->sys_op);
+
 	memset(p,0,PCHAT_LEN);
 	write(in,p,PCHAT_LEN);
 	write(out,p,PCHAT_LEN);
@@ -176,7 +177,7 @@ int chat(scfg_t *cfg, int nodenum, node_t *node, box_t *boxch, void(*timecallbac
 	togglechat(cfg,nodenum,node,TRUE);
 
 	while(in != -1) {
-		
+
 		now=time(NULL);
 		if(now!=last_nodechk) {
 
diff --git a/src/sbbs3/umonitor/umonitor.c b/src/sbbs3/umonitor/umonitor.c
index 8c5e9ef994..6613c40205 100644
--- a/src/sbbs3/umonitor/umonitor.c
+++ b/src/sbbs3/umonitor/umonitor.c
@@ -1192,8 +1192,11 @@ USAGE:
 				uifc.msg("Error reading node data!");
 				continue;
 			}
-			if((node.status==NODE_INUSE) && node.useron)
-				chat(&cfg,main_dflt,&node,&boxch,NULL);
+			if((node.status==NODE_INUSE) && node.useron) {
+				int result = chat(&cfg,main_dflt,&node,&boxch,NULL);
+				if(result != 0)
+					uifc.msgf("Chat error: %d (%s)", result, strerror(errno));
+			}
 			continue;
 		}
 
@@ -1305,9 +1308,12 @@ USAGE:
 							break;
 
 						case 3: /* Chat with User */
-							chat(&cfg,main_dflt,&node,&boxch,NULL);
+						{
+							int result = chat(&cfg,main_dflt,&node,&boxch,NULL);
+							if(result != 0)
+								uifc.msgf("Chat error %d (%s)", result, strerror(errno));
 							break;
-
+						}
 						case 4: /* Node Toggles */
 							node_toggles(&cfg, j);
 							break;
-- 
GitLab


From e4dc010143a1ff164b148b4cc3c8f44e52d5a5f2 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Thu, 17 Sep 2020 17:49:01 -0700
Subject: [PATCH 713/752] Don't send unsupported key-strokes (e.g. ESC) to the
 chat partner

---
 src/sbbs3/chat.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp
index 4345cc8faf..40c9ad5fbb 100644
--- a/src/sbbs3/chat.cpp
+++ b/src/sbbs3/chat.cpp
@@ -1065,6 +1065,8 @@ void sbbs_t::privchat(bool local)
 					}
 					// SYNC;
 				} 
+			} else { // illegal key
+				continue;
 			}
 
 			read(out,&c,1);
-- 
GitLab


From 2d2357d9243d0ddc393ebe32f1379212b6c6c0ee Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 19:17:59 -0700
Subject: [PATCH 714/752] strcpy() -> SAFECOPY()

---
 src/sbbs3/chat.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp
index 40c9ad5fbb..d31137dda5 100644
--- a/src/sbbs3/chat.cpp
+++ b/src/sbbs3/chat.cpp
@@ -1270,7 +1270,7 @@ int sbbs_t::getnodetopage(int all, int telegram)
 		sprintf(str,text[NodeToPrivateChat],lastnodemsg);
 	mnemonics(str);
 
-	strcpy(str,lastnodemsguser);
+	SAFECOPY(str,lastnodemsguser);
 	getstr(str,LEN_ALIAS,K_UPRLWR|K_LINE|K_EDIT|K_AUTODEL);
 	if(sys_status&SS_ABORT) {
 		sys_status&= ~SS_ABORT;
@@ -1291,7 +1291,7 @@ int sbbs_t::getnodetopage(int all, int telegram)
 				? text[UNKNOWN_USER] : username(&cfg,node.useron,tmp));
 			return(0); 
 		}
-		strcpy(lastnodemsguser,str);
+		SAFECOPY(lastnodemsguser,str);
 		if(telegram)
 			return(node.useron);
 		return(j); 
@@ -1332,7 +1332,7 @@ int sbbs_t::getnodetopage(int all, int telegram)
 			}
 			if(telegram)
 				return(j);
-			strcpy(lastnodemsguser,str);
+			SAFECOPY(lastnodemsguser,str);
 			return(i); 
 		} 
 	}
-- 
GitLab


From 8c4111605cdd2ab6d8913ad2b3a19118b290ddcc Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 20:41:18 -0700
Subject: [PATCH 715/752] When a sysop pages a node for private chat, force
 that node into private chat.

When a sysop wants to chat, he wants to chat right now, damnit!
---
 src/sbbs3/chat.cpp    | 69 +++++++++++++++++++++++--------------------
 src/sbbs3/getnode.cpp | 29 +++++++++++++++++-
 src/sbbs3/node.c      |  2 ++
 src/sbbs3/nodedefs.h  |  1 +
 src/sbbs3/sbbs.h      |  2 +-
 5 files changed, 69 insertions(+), 34 deletions(-)

diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp
index d31137dda5..aabbee78c8 100644
--- a/src/sbbs3/chat.cpp
+++ b/src/sbbs3/chat.cpp
@@ -771,7 +771,7 @@ bool sbbs_t::chan_access(uint cnum)
 /****************************************************************************/
 /* Private split-screen (or interspersed) chat with node or local sysop		*/
 /****************************************************************************/
-void sbbs_t::privchat(bool local)
+void sbbs_t::privchat(bool forced, int node_num)
 {
 	char	str[128],c,*p,localbuf[5][81],remotebuf[5][81]
 			,localline=0,remoteline=0,localchar=0,remotechar=0
@@ -787,8 +787,8 @@ void sbbs_t::privchat(bool local)
 	node_t	node;
 	time_t	last_nodechk=0;
 
-	if(local) 
-		n=0;
+	if(forced)
+		n = node_num;
 	else {
 
 		if(useron.rest&FLAG('C')) {
@@ -808,23 +808,28 @@ void sbbs_t::privchat(bool local)
 			bprintf(text[NodeNAlreadyInPChat],n);
 			return; 
 		}
-		if((node.action!=NODE_PAGE || node.aux!=cfg.node_num)
-			&& node.misc&NODE_POFF && !SYSOP) {
-			bprintf(text[CantPageNode],node.misc&NODE_ANON
-				? text[UNKNOWN_USER] : username(&cfg,node.useron,tmp));
-			return; 
-		}
-		if(node.action!=NODE_PAGE) {
-			bprintf(text[PagingUser]
-				,node.misc&NODE_ANON ? text[UNKNOWN_USER] : username(&cfg,node.useron,tmp)
-				,node.misc&NODE_ANON ? 0 : node.useron);
-			sprintf(str,text[NodePChatPageMsg]
-				,cfg.node_num,thisnode.misc&NODE_ANON
-					? text[UNKNOWN_USER] : useron.alias);
-			putnmsg(&cfg,n,str);
-			sprintf(str,"paged %s on node %d to private chat"
-				,username(&cfg,node.useron,tmp),n);
-			logline("C",str); 
+		if(SYSOP && getnodedat(n, &node, true) == 0) {
+			node.misc |= NODE_FCHAT;
+			putnodedat(n, &node);
+		} else {
+			if((node.action!=NODE_PAGE || node.aux!=cfg.node_num)
+				&& node.misc&NODE_POFF) {
+				bprintf(text[CantPageNode],node.misc&NODE_ANON
+					? text[UNKNOWN_USER] : username(&cfg,node.useron,tmp));
+				return;
+			}
+			if(node.action!=NODE_PAGE) {
+				bprintf(text[PagingUser]
+					,node.misc&NODE_ANON ? text[UNKNOWN_USER] : username(&cfg,node.useron,tmp)
+					,node.misc&NODE_ANON ? 0 : node.useron);
+				sprintf(str,text[NodePChatPageMsg]
+					,cfg.node_num,thisnode.misc&NODE_ANON
+						? text[UNKNOWN_USER] : useron.alias);
+				putnmsg(&cfg,n,str);
+				sprintf(str,"paged %s on node %d to private chat"
+					,username(&cfg,node.useron,tmp),n);
+				logline("C",str);
+			}
 		}
 
 		if(getnodedat(cfg.node_num,&thisnode,true)==0) {
@@ -858,14 +863,14 @@ void sbbs_t::privchat(bool local)
 	if(getnodedat(cfg.node_num,&thisnode,true)==0) {
 		thisnode.action=action=NODE_PCHT;
 		thisnode.aux=n;
-		thisnode.misc&=~NODE_LCHAT;
+		thisnode.misc&=~ (NODE_LCHAT|NODE_FCHAT);
 		putnodedat(cfg.node_num,&thisnode);
 	}
 
-	if(!online || sys_status&SS_ABORT)
+	if(!online || (!forced && (sys_status&SS_ABORT)))
 		return;
 
-	if(local) {
+	if(forced && n == 0) {
 		/* If an external sysop chat event handler is installed, just run that and do nothing else */
 		if(user_event(EVENT_LOCAL_CHAT))
 			return;
@@ -882,7 +887,7 @@ void sbbs_t::privchat(bool local)
 	*/
 
 	if(!(sys_status&SS_SPLITP)) {
-		if(local)
+		if(forced)
 			bprintf(text[SysopIsHere],cfg.sys_op);
 		else
 			bputs(text[WelcomeToPrivateChat]);
@@ -894,7 +899,7 @@ void sbbs_t::privchat(bool local)
 		return; 
 	}
 
-	if(local)
+	if(forced && n == 0)
 		sprintf(inpath,"%slchat.dab",cfg.node_dir);
 	else
 		sprintf(inpath,"%schat.dab",cfg.node_path[n-1]);
@@ -924,7 +929,7 @@ void sbbs_t::privchat(bool local)
 		putnodedat(cfg.node_num,&thisnode);
 	}
 
-	if(!local) {
+	if(n) { // not local
 		if(getnodedat(n,&node,true)==0) {
 			node.misc|=NODE_RPCHT;				/* Set "reset pchat flag" */
 			putnodedat(n,&node); 				/* on other node */
@@ -955,7 +960,7 @@ void sbbs_t::privchat(bool local)
 		ansi_save();
 		ansi_gotoxy(1,13);
 		remote_y=1;
-		bprintf(local ? local_sep : sep
+		bprintf(forced ? local_sep : sep
 			,thisnode.misc&NODE_MSGW ? 'T':' '
 			,sectostr(timeleft,tmp)
 			,thisnode.misc&NODE_NMSG ? 'M':' ');
@@ -963,7 +968,7 @@ void sbbs_t::privchat(bool local)
 		local_y=14; 
 	}
 
-	while(online && (local || !(sys_status&SS_ABORT))) {
+	while(online && (forced || !(sys_status&SS_ABORT))) {
 		lncntr=0;
 		if(sys_status&SS_SPLITP)
 			lbuflen=0;
@@ -1007,7 +1012,7 @@ void sbbs_t::privchat(bool local)
 					bputs("\1i_\1n");  /* Fake cursor */
 					ansi_save();
 					ansi_gotoxy(1,13);
-					bprintf(local ? local_sep : sep
+					bprintf(forced ? local_sep : sep
 						,thisnode.misc&NODE_MSGW ? 'T':' '
 						,sectostr(timeleft,tmp)
 						,thisnode.misc&NODE_NMSG ? 'M':' ');
@@ -1037,7 +1042,7 @@ void sbbs_t::privchat(bool local)
 
 					if(sys_status&SS_SPLITP && local_y==24) {
 						ansi_gotoxy(1,13);
-						bprintf(local ? local_sep : sep
+						bprintf(forced ? local_sep : sep
 							,thisnode.misc&NODE_MSGW ? 'T':' '
 							,sectostr(timeleft,tmp)
 							,thisnode.misc&NODE_NMSG ? 'M':' ');
@@ -1133,7 +1138,7 @@ void sbbs_t::privchat(bool local)
 
 					if(sys_status&SS_SPLITP && remote_y==12) {
 						CRLF;
-						bprintf(local ? local_sep : sep
+						bprintf(forced ? local_sep : sep
 							,thisnode.misc&NODE_MSGW ? 'T':' '
 							,sectostr(timeleft,tmp)
 							,thisnode.misc&NODE_NMSG ? 'M':' ');
@@ -1194,7 +1199,7 @@ void sbbs_t::privchat(bool local)
 					nodesync(); 
 			}
 
-			if(!local) {
+			if(n != 0) {
 				getnodedat(n,&node,0);
 				if((node.action!=NODE_PCHT && node.action!=NODE_PAGE)
 					|| node.aux!=cfg.node_num) {
diff --git a/src/sbbs3/getnode.cpp b/src/sbbs3/getnode.cpp
index b38d88e5ca..89e40b1c74 100644
--- a/src/sbbs3/getnode.cpp
+++ b/src/sbbs3/getnode.cpp
@@ -109,6 +109,18 @@ int sbbs_t::getnodedat(uint number, node_t *node, bool lockit)
 	return(0);
 }
 
+static int getpagingnode(scfg_t* cfg)
+{
+	for(int i = 1; i <= cfg->sys_nodes; i++) {
+		node_t node;
+		if(i == cfg->node_num)
+			continue;
+		if(getnodedat(cfg, i, &node, FALSE, NULL) == 0 && node.action == NODE_PAGE && node.aux == cfg->node_num)
+			return i;
+	}
+	return 0;
+}
+
 /****************************************************************************/
 /* Synchronizes all the nodes knowledge of the other nodes' actions, mode,  */
 /* status and other flags.                                                  */
@@ -184,6 +196,19 @@ void sbbs_t::nodesync(bool clearline)
 		privchat(true);
 		RESTORELINE;
 	}
+
+	if(thisnode.misc&NODE_FCHAT) { // forced into private chat
+		int n = getpagingnode(&cfg);
+		if(n) {
+			SAVELINE;
+			privchat(true, n);
+			RESTORELINE;
+		}
+		if(getnodedat(cfg.node_num, &thisnode, true)==0) {
+			thisnode.misc &= ~NODE_FCHAT;
+			putnodedat(cfg.node_num, &thisnode); 
+		}
+	}
 		
 	if(sys_status&SS_USERON && memcmp(&nodesync_user,&useron,sizeof(user_t))) {
 		getusrdirs();
@@ -644,7 +669,7 @@ void sbbs_t::printnodedat(uint number, node_t* node)
 		outchar(')'); 
 	}
 	if(SYSOP && ((node->misc
-		&(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN|NODE_LCHAT))
+		&(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN|NODE_LCHAT|NODE_FCHAT))
 		|| node->status==NODE_QUIET)) {
 		bputs(" [");
 		if(node->misc&NODE_ANON)
@@ -663,6 +688,8 @@ void sbbs_t::printnodedat(uint number, node_t* node)
 			outchar('D');
 		if(node->misc&NODE_LCHAT)
 			outchar('C');
+		if(node->misc&NODE_FCHAT)
+			outchar('F');
 		outchar(']'); 
 	}
 	if(node->errors && SYSOP) {
diff --git a/src/sbbs3/node.c b/src/sbbs3/node.c
index b95966641f..e23fd52e8c 100644
--- a/src/sbbs3/node.c
+++ b/src/sbbs3/node.c
@@ -438,6 +438,8 @@ void printnodedat(int number, node_t node)
 			putchar('D');
 		if(node.misc&NODE_LCHAT)
 			putchar('C');
+		if(node.misc&NODE_FCHAT)
+			putchar('F');
 		putchar(']'); }
 	if(node.errors)
 		printf(" %d error%c",node.errors, node.errors>1 ? 's' : '\0' );
diff --git a/src/sbbs3/nodedefs.h b/src/sbbs3/nodedefs.h
index b6fc735d54..7e3a829252 100644
--- a/src/sbbs3/nodedefs.h
+++ b/src/sbbs3/nodedefs.h
@@ -76,6 +76,7 @@ enum node_status {                  /* Node Status */
 #define NODE_NMSG   (1<<11)         /* Node message waiting (new way) */
 #define NODE_EXT    (1<<12)         /* Extended info on node action */
 #define NODE_LCHAT	(1<<13)			/* Being pulled into local chat */
+#define NODE_FCHAT	(1<<14)			/* Being forced into private chat */
 
 enum node_action {                  /* Node Action */
      NODE_MAIN                      /* Main Prompt */
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index 062e974718..31ff5d28d2 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -927,7 +927,7 @@ public:
 	void	localguru(char *guru, int gurunum);
 	bool	sysop_page(void);
 	bool	guru_page(void);
-	void	privchat(bool local=false);
+	void	privchat(bool forced=false, int node_num=0);
 	bool	chan_access(uint cnum);
 	int		getnodetopage(int all, int telegram);
 
-- 
GitLab


From 2ca49b58adf161233160491a9d376b869acb319c Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 20:58:53 -0700
Subject: [PATCH 716/752] Fix an off-by-one line bug in the "local side" of the
 split-screen-chat.

This bug would cause the split-screen separator to repeat when the local
side scrolled (if the terminal was 24 rows, bug didn't happen when local
screen was 25 rows).

This bug appears to have "always" been there. The split-screen separator is
80 columns, so that forced a line wrap on the subsequent CRLF and the local_y
value was "off-by-one". Just use ansi_gotoxy() instead and put the cursor
where you think it is.

Also, support > 24 row terminals for longer chat history (bottom half only).
---
 src/sbbs3/chat.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp
index aabbee78c8..ce9ed96bbc 100644
--- a/src/sbbs3/chat.cpp
+++ b/src/sbbs3/chat.cpp
@@ -964,7 +964,7 @@ void sbbs_t::privchat(bool forced, int node_num)
 			,thisnode.misc&NODE_MSGW ? 'T':' '
 			,sectostr(timeleft,tmp)
 			,thisnode.misc&NODE_NMSG ? 'M':' ');
-		CRLF;
+		ansi_gotoxy(1,14);
 		local_y=14; 
 	}
 
@@ -1016,7 +1016,7 @@ void sbbs_t::privchat(bool forced, int node_num)
 						,thisnode.misc&NODE_MSGW ? 'T':' '
 						,sectostr(timeleft,tmp)
 						,thisnode.misc&NODE_NMSG ? 'M':' ');
-					CRLF;
+					ansi_gotoxy(1,14);
 					attr(cfg.color[clr_chatlocal]);
 					localbuf[localline][localchar]=0;
 					for(i=0;i<=localline;i++) {
@@ -1040,7 +1040,7 @@ void sbbs_t::privchat(bool forced, int node_num)
 					localbuf[localline][localchar]=0;
 					localchar=0;
 
-					if(sys_status&SS_SPLITP && local_y==24) {
+					if(sys_status&SS_SPLITP && local_y >= rows) {
 						ansi_gotoxy(1,13);
 						bprintf(forced ? local_sep : sep
 							,thisnode.misc&NODE_MSGW ? 'T':' '
-- 
GitLab


From 7083ff528f81703ab000d0e70fc1729224117af2 Mon Sep 17 00:00:00 2001
From: Rob Swindell <admin@example.com>
Date: Thu, 17 Sep 2020 21:10:21 -0700
Subject: [PATCH 717/752] Don't cache .o files. This appears to not work 100%
 (e.g. it's always rebuilding cryptlib anyway and it failed to re-compile some
 of libsbbs.so resulting in a link failure).

---
 .gitlab-ci.yml | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8beb308f56..e204c87971 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,11 +4,6 @@
 # see https://hub.docker.com/_/gcc/
 image: gcc
 
-# cache outputs to reduce the build time
-cache:
-    paths:
-        - "**/*.o"
-
 build-sbbs:
   stage: build
   # instead of calling g++ directly you can also use some build toolkit like make
-- 
GitLab


From 2c24cf40cf815d5136bede6bbe1ea28e4ad6af1d Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 21:52:28 -0700
Subject: [PATCH 718/752] List all the new @-codes and the (bran new) sysop
 command: AVAIL

---
 docs/v318_new.txt | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/docs/v318_new.txt b/docs/v318_new.txt
index 05ca779db6..e4f1738430 100644
--- a/docs/v318_new.txt
+++ b/docs/v318_new.txt
@@ -25,9 +25,21 @@ o Automatic text-portion of multi-part MIME-messages in QWK message packets
 o New operator option to delete numeric ranges of messages in sub-boards
 o Configurable treatment of "Soft-CRs" created by external message editors
 o @-codes:
-  - Many added (including some additional PCBoard codes)
-  - New output formatting options
+  - New formatting options: http://wiki.synchro.net/custom:atcodes#formatting
+  - UNICODE http://wiki.synchro.net/custom:atcodes#unicode
+  - Mouse "hot spot" control (HOT, CLEAR_HOT, ~, `)
+  - Many added (including some additional PCBoard codes):
+    CHECKMARK, ELLIPSIS, COPY, SOUNDCOPY, REGISTERED, TRADEMARK, DEGREE_C,
+    WIDE, CHARSET, NN, TN, AN, ON, PWDAYS, AUTODEL, DATETIMEZONE, DATEFMT,
+    FILE, FILESIZE, FILEBYTES, FILEKB, FILEMB, FILEGB, FILL, POS, DELAY,
+    YESCHAR, NOCHAR, QUITCHAR, BPS:<rate>, PWAGE, PWDATE, MPERC, MPERD,
+    MAXCALLS, MAXPOSTS, MAXMAILS, MAXLINES, FIRSTON, FIRSTDATEON, FIRSTTIMEON,
+    EMAILS, FBACKS, ETODAY, PTODAY, LTODAY, MTODAY, MTOTAL, TTODAY, TTOTAL,
+    TLAST, MEXTRA, TEXTRA, MBANKED, TBANKED, PCR, CREDITS, FREECDT,
+    REALNAME, STR, STRVAR, JS:<prop>, CLREOL, MAILW#, MAILP#, SPAMW#,
+    MSG_IMP_DATE, MSG_IMP_TIMEZONE, SYSAVAIL
 o New helpful sysop debugging command: ECHO and EVAL
+o New sysop command to toggle chat availability: AVAIL
 o Auto-ZMODEM upload of message text from user
  (internal editor and fseditor.js)
 o sexyz:
-- 
GitLab


From 66191a24f8569ab0b88dbffc7313f5580517d39e Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Thu, 17 Sep 2020 22:21:28 -0700
Subject: [PATCH 719/752] Look for the json-db.ini file in the ctrl dir where
 Matt said it should be

---
 exec/load/json-db.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/load/json-db.js b/exec/load/json-db.js
index 2b07022a36..dd4781ead1 100644
--- a/exec/load/json-db.js
+++ b/exec/load/json-db.js
@@ -68,7 +68,7 @@ function JSONdb (fileName, scope) {
 	/* database settings */
 	this.settings={
 		/* misc settings */
-		FILE:system.data_dir + "json-db.ini",
+		FILE:system.ctrl_dir + "json-db.ini",
 		FILE_BUFFER:524288,
 		LAST_SAVE:-1,
 		SAVE_INTERVAL:-1,
-- 
GitLab


From be0c7116dc5b1c463932c40b23036ae1ba0f98d1 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Fri, 18 Sep 2020 23:00:51 -0700
Subject: [PATCH 720/752] Use system.min/max_password_length properties

---
 webv4/pages/.examples/000-register.xjs | 8 ++++----
 webv4/root/api/register.ssjs           | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/webv4/pages/.examples/000-register.xjs b/webv4/pages/.examples/000-register.xjs
index e9f6b187fb..2ecaafe25d 100644
--- a/webv4/pages/.examples/000-register.xjs
+++ b/webv4/pages/.examples/000-register.xjs
@@ -67,10 +67,10 @@
 				<?xjs write(locale.strings.page_register.input_password); ?>
 			</label>
 			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
-			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password1" name="password1" placeholder="<?xjs write(locale.strings.page_register.input_password); ?>" required>
+			<input type="password" data-minlength="<?xjs write(system.min_password_length) ?>" maxlength="<?xjs write(system.max_password_length) ?>" class="form-control" id="password1" name="password1" placeholder="<?xjs write(locale.strings.page_register.input_password); ?>" required>
 			<span class="help-block">
-				<?xjs write(format(locale.strings.page_register.help_text_minimum_characters, settings.minimum_password_length)); ?>,
-				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, LEN_PASS)); ?>
+				<?xjs write(format(locale.strings.page_register.help_text_minimum_characters, system.min_password_length)); ?>,
+				<?xjs write(format(locale.strings.page_register.help_text_maximum_characters, system.max_password_length)); ?>
 			</span>
 		</div>
 			<div class="form-group">
@@ -78,7 +78,7 @@
 				<?xjs write(locale.strings.page_register.input_password_confirm); ?>
 			</label>
 			<span title="<?xjs write(locale.strings.page_register.label_field_required); ?>" class="glyphicon glyphicon-asterisk"></span>
-			<input type="password" data-minlength="4" maxlength="8" class="form-control" id="password2" name="password2" placeholder="<?xjs write(locale.strings.page_register.input_password_confirm); ?>" data-match="#password1" required>
+			<input type="password" data-minlength="<?xjs write(system.min_password_length) ?>" maxlength="<?xjs write(system.max_password_length) ?>" class="form-control" id="password2" name="password2" placeholder="<?xjs write(locale.strings.page_register.input_password_confirm); ?>" data-match="#password1" required>
 		</div>
 
 		<div class="form-group">
diff --git a/webv4/root/api/register.ssjs b/webv4/root/api/register.ssjs
index 0749730ddc..350a084e62 100644
--- a/webv4/root/api/register.ssjs
+++ b/webv4/root/api/register.ssjs
@@ -101,8 +101,8 @@ if (!valid_param('alias', MIN_ALIAS, LEN_ALIAS) || !system.check_name(clean_para
 
 if (!Request.has_param('password1') || !Request.has_param('password2') || clean_param('password1') != clean_param('password2')) {
 	reply.errors.push(locale.strings.api_register.error_password_mismatch);
-} else if (!in_range(clean_param('password1').length, settings.minimum_password_length, LEN_PASS)) {
-	reply.errors.push(format(locale.strings.api_register.error_password_length, settings.minimum_password_length, LEN_PASS));
+} else if (!in_range(clean_param('password1').length, system.min_password_length, system.max_password_length)) {
+	reply.errors.push(format(locale.strings.api_register.error_password_length, system.min_password_length, system.max_password_length));
 } else {
 	prepUser.password = clean_param('password1');
 }
-- 
GitLab


From 8e3a77d455ad9f360a4023780e13fb04f742bb19 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 00:16:38 -0700
Subject: [PATCH 721/752] Allow the sysop to configure the minimum user
 password length betwen 4 (the default) and 40 (the maximum) characters

---
 src/sbbs3/js_system.c    |  2 +-
 src/sbbs3/logon.cpp      |  2 +-
 src/sbbs3/newuser.cpp    |  2 +-
 src/sbbs3/scfg/scfgsys.c | 24 ++++++++++++++++++++----
 src/sbbs3/scfgdefs.h     |  1 +
 src/sbbs3/scfglib1.c     |  8 +++++++-
 src/sbbs3/scfgsave.c     |  4 +++-
 src/sbbs3/str.cpp        |  2 +-
 src/sbbs3/useredit.cpp   |  2 +-
 9 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c
index 937bc42942..583172f39b 100644
--- a/src/sbbs3/js_system.c
+++ b/src/sbbs3/js_system.c
@@ -176,7 +176,7 @@ static JSBool js_system_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 			*vp = INT_TO_JSVAL(cfg->sys_pwdays);
 			break;
 		case SYS_PROP_MINPWLEN:
-			*vp = INT_TO_JSVAL(MIN_PASS_LEN);
+			*vp = INT_TO_JSVAL(cfg->min_pwlen);
 			break;
 		case SYS_PROP_MAXPWLEN:
 			*vp = INT_TO_JSVAL(LEN_PASS);
diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index 997e309bdf..afe1fc5a4c 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -248,7 +248,7 @@ bool sbbs_t::logon()
 
 			if(cfg.sys_misc&SM_PWEDIT && yesno(text[NewPasswordQ]))
 				while(online) {
-					bprintf(text[NewPasswordPromptFmt], MIN_PASS_LEN, LEN_PASS);
+					bprintf(text[NewPasswordPromptFmt], cfg->min_pwlen, LEN_PASS);
 					getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM);
 					truncsp(str);
 					if(chkpass(str,&useron,true))
diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp
index da773b4116..985d93a3e8 100644
--- a/src/sbbs3/newuser.cpp
+++ b/src/sbbs3/newuser.cpp
@@ -386,7 +386,7 @@ BOOL sbbs_t::newuser()
 
 		if(cfg.sys_misc&SM_PWEDIT && text[NewPasswordQ][0] && yesno(text[NewPasswordQ]))
 			while(online) {
-				bprintf(text[NewPasswordPromptFmt], MIN_PASS_LEN, LEN_PASS);
+				bprintf(text[NewPasswordPromptFmt], cfg->min_pwlen, LEN_PASS);
 				getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM);
 				truncsp(str);
 				if(chkpass(str,&useron,true)) {
diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index f2e009333c..53c526cf2d 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -99,15 +99,17 @@ void sys_cfg(void)
 		sprintf(opt[i++],"%-33.33s%s","Operator",cfg.sys_op);
 		sprintf(opt[i++],"%-33.33s%s","Password","**********");
 
-		sprintf(str,"%s Password"
+		SAFEPRINTF(str,"%s Password"
 			,cfg.sys_misc&SM_PWEDIT && cfg.sys_pwdays ? "Users Must Change"
 			: cfg.sys_pwdays ? "Users Get New Random" : "Users Can Change");
 		if(cfg.sys_pwdays)
-			sprintf(tmp,"Every %u Days",cfg.sys_pwdays);
+			SAFEPRINTF(tmp,"Every %u Days",cfg.sys_pwdays);
 		else if(cfg.sys_misc&SM_PWEDIT)
-			strcpy(tmp,"Yes");
+			SAFECOPY(tmp,"Yes");
 		else
-			strcpy(tmp,"No");
+			SAFECOPY(tmp,"No");
+		if(cfg.sys_misc&SM_PWEDIT)
+			sprintf(tmp + strlen(tmp), ", %u chars minimum", cfg.min_pwlen);
 		sprintf(opt[i++],"%-33.33s%s",str,tmp);
 
 		sprintf(opt[i++],"%-33.33s%u","Days to Preserve Deleted Users"
@@ -427,6 +429,20 @@ void sys_cfg(void)
 				else if(i==1 && cfg.sys_misc&SM_PWEDIT) {
 					cfg.sys_misc&=~SM_PWEDIT;
 					uifc.changes=1; 
+				} else if(i == -1)
+					break;
+
+				if(cfg.sys_misc&SM_PWEDIT) {
+					SAFEPRINTF(tmp, "%u", cfg.min_pwlen);
+					SAFEPRINTF2(str, "Minimum Password Length (between %u and %u)", MIN_PASS_LEN, LEN_PASS);
+					if(uifc.input(WIN_MID|WIN_SAV,0,0, str
+						,tmp, 2, K_NUMBER|K_EDIT) < 1)
+						break;
+					cfg.min_pwlen=atoi(tmp);
+					if(cfg.min_pwlen < MIN_PASS_LEN)
+						cfg.min_pwlen = MIN_PASS_LEN;
+					if(cfg.min_pwlen > LEN_PASS)
+						cfg.min_pwlen = LEN_PASS;
 				}
 				i = cfg.sys_pwdays ? 0 : 1;
 				uifc.helpbuf=
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index b5481b3144..84d28f8a83 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -456,6 +456,7 @@ typedef struct
 	char 			sys_daily[LEN_CMD+1];	   /* Daily event */
 	char 			sys_logon[LEN_CMD+1];	   /* Logon event */
 	char 			sys_logout[LEN_CMD+1];	   /* Logoff event */
+	uint8_t			min_pwlen;
 	uint16_t		sys_pwdays; 		/* Max days between password change */
 	uint16_t		sys_deldays;		/* Days to keep deleted users */
 	uint16_t		sys_autodel;		/* Autodeletion after x days inactive */
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 1f27f294f2..2b8e2210a6 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -233,7 +233,13 @@ BOOL read_main_cfg(scfg_t* cfg, char* error)
 	get_int(cfg->new_install,instream);
 	get_int(cfg->new_msgscan_init,instream);
 	get_int(cfg->guest_msgscan_init,instream);
-	for(i=0;i<5;i++)
+	get_int(cfg->min_pwlen, instream);
+	if(cfg->min_pwlen < MIN_PASS_LEN)
+		cfg->min_pwlen = MIN_PASS_LEN;
+	if(cfg->min_pwlen > LEN_PASS)
+		cfg->min_pwlen = LEN_PASS;
+	get_int(c, instream);
+	for(i=0;i<4;i++)
 		get_int(n,instream);
 
 	/*************************/
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index f86bde4a97..27c29b6020 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -220,8 +220,10 @@ BOOL DLLCALL write_main_cfg(scfg_t* cfg, int backup_level)
 	put_int(cfg->new_install,stream);
 	put_int(cfg->new_msgscan_init, stream);
 	put_int(cfg->guest_msgscan_init, stream);
+	put_int(cfg->min_pwlen, stream);
+	put_int(c, stream);
 	n=0;
-	for(i=0;i<5;i++)
+	for(i=0;i<4;i++)
 		put_int(n,stream);
 
 	put_int(cfg->expired_level,stream);
diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp
index 6607442ebf..827cc2f003 100644
--- a/src/sbbs3/str.cpp
+++ b/src/sbbs3/str.cpp
@@ -702,7 +702,7 @@ bool sbbs_t::chkpass(char *passwd, user_t* user, bool unique)
 	SAFECOPY(pass,passwd);
 	strupr(pass);
 
-	if(strlen(pass) < MIN_PASS_LEN) {
+	if(strlen(pass) < cfg->min_pwlen) {
 		bputs(text[PasswordTooShort]);
 		return(false); 
 	}
diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp
index ee055e4cf6..5fbc07653e 100644
--- a/src/sbbs3/useredit.cpp
+++ b/src/sbbs3/useredit.cpp
@@ -1112,7 +1112,7 @@ void sbbs_t::maindflts(user_t* user)
 						pause();
 						break; 
 					}
-					bprintf(text[NewPasswordPromptFmt], MIN_PASS_LEN, LEN_PASS);
+					bprintf(text[NewPasswordPromptFmt], cfg->min_pwlen, LEN_PASS);
 					if(!getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM))
 						break;
 					truncsp(str);
-- 
GitLab


From c79f1e4b92ae49853ebd3c7bbf0fba84fc8cbf8d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 00:39:49 -0700
Subject: [PATCH 722/752] Fix compile error introduced in previous commit.

---
 src/sbbs3/logon.cpp    | 2 +-
 src/sbbs3/newuser.cpp  | 2 +-
 src/sbbs3/str.cpp      | 2 +-
 src/sbbs3/useredit.cpp | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index afe1fc5a4c..ce7758cefa 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -248,7 +248,7 @@ bool sbbs_t::logon()
 
 			if(cfg.sys_misc&SM_PWEDIT && yesno(text[NewPasswordQ]))
 				while(online) {
-					bprintf(text[NewPasswordPromptFmt], cfg->min_pwlen, LEN_PASS);
+					bprintf(text[NewPasswordPromptFmt], cfg.min_pwlen, LEN_PASS);
 					getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM);
 					truncsp(str);
 					if(chkpass(str,&useron,true))
diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp
index 985d93a3e8..4ba1928680 100644
--- a/src/sbbs3/newuser.cpp
+++ b/src/sbbs3/newuser.cpp
@@ -386,7 +386,7 @@ BOOL sbbs_t::newuser()
 
 		if(cfg.sys_misc&SM_PWEDIT && text[NewPasswordQ][0] && yesno(text[NewPasswordQ]))
 			while(online) {
-				bprintf(text[NewPasswordPromptFmt], cfg->min_pwlen, LEN_PASS);
+				bprintf(text[NewPasswordPromptFmt], cfg.min_pwlen, LEN_PASS);
 				getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM);
 				truncsp(str);
 				if(chkpass(str,&useron,true)) {
diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp
index 827cc2f003..6c6f4e2424 100644
--- a/src/sbbs3/str.cpp
+++ b/src/sbbs3/str.cpp
@@ -702,7 +702,7 @@ bool sbbs_t::chkpass(char *passwd, user_t* user, bool unique)
 	SAFECOPY(pass,passwd);
 	strupr(pass);
 
-	if(strlen(pass) < cfg->min_pwlen) {
+	if(strlen(pass) < cfg.min_pwlen) {
 		bputs(text[PasswordTooShort]);
 		return(false); 
 	}
diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp
index 5fbc07653e..76c10af857 100644
--- a/src/sbbs3/useredit.cpp
+++ b/src/sbbs3/useredit.cpp
@@ -1112,7 +1112,7 @@ void sbbs_t::maindflts(user_t* user)
 						pause();
 						break; 
 					}
-					bprintf(text[NewPasswordPromptFmt], cfg->min_pwlen, LEN_PASS);
+					bprintf(text[NewPasswordPromptFmt], cfg.min_pwlen, LEN_PASS);
 					if(!getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM))
 						break;
 					truncsp(str);
-- 
GitLab


From 6582b952dc0ed45dc572ff99f0397455d2871e61 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 00:44:07 -0700
Subject: [PATCH 723/752] Ignore the artifacts and custom files in webv4 Mostly
 copied from
 https://github.com/echicken/synchronet-web-v4/blob/master/.gitignore

---
 webv4/.gitignore | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 webv4/.gitignore

diff --git a/webv4/.gitignore b/webv4/.gitignore
new file mode 100644
index 0000000000..e120441f21
--- /dev/null
+++ b/webv4/.gitignore
@@ -0,0 +1,18 @@
+# Ignore processed XJS files
+*.xjs.ssjs
+
+# Ignore non-example pages
+pages/*
+!pages/.examples
+
+# Ignore non-example sidebar modules
+sidebar/*
+!sidebar/.examples
+
+# Ignore non-example components
+components/*
+!components/.examples
+
+root/css/custom.css
+root/logo.gif
+
-- 
GitLab


From 90e5eb4e47fc8797db2f79a1d6fda7d426ed369d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 01:17:22 -0700
Subject: [PATCH 724/752] Address unsafe string format warnings from gcc
 (Debian 8.3.0-6) 8.3.0

---
 src/sbbs3/addfiles.c   |  4 ++--
 src/sbbs3/allusers.c   |  6 +++---
 src/sbbs3/chat.cpp     |  2 +-
 src/sbbs3/delfiles.c   |  4 ++--
 src/sbbs3/dstsedit.c   |  4 ++--
 src/sbbs3/fido.cpp     |  2 +-
 src/sbbs3/filelist.c   |  4 ++--
 src/sbbs3/fmsgdump.c   |  2 +-
 src/sbbs3/js_bbs.cpp   |  2 +-
 src/sbbs3/listfile.cpp | 14 +++++++-------
 src/sbbs3/netmail.cpp  |  4 ++--
 src/sbbs3/qwknodes.c   |  2 +-
 src/sbbs3/sbbscon.c    | 14 +++++++-------
 src/sbbs3/services.c   |  8 ++++----
 src/sbbs3/slog.c       |  2 +-
 src/sbbs3/un_qwk.cpp   |  2 +-
 src/sbbs3/upload.cpp   |  6 +++---
 src/sbbs3/websrvr.c    | 15 ++++++++-------
 src/sbbs3/xtrn_sec.cpp | 18 +++++++++---------
 19 files changed, 58 insertions(+), 57 deletions(-)

diff --git a/src/sbbs3/addfiles.c b/src/sbbs3/addfiles.c
index 73fc9442e9..5dabc3ba37 100644
--- a/src/sbbs3/addfiles.c
+++ b/src/sbbs3/addfiles.c
@@ -1000,7 +1000,7 @@ int main(int argc, char **argv)
 					synclist(str,i);
 				continue;
 			}
-			sprintf(str,"%s%s",scfg.dir[f.dir]->path,auto_name);
+			SAFEPRINTF2(str,"%s%s",scfg.dir[f.dir]->path,auto_name);
 			if(fexistcase(str) && flength(str)>0L) {
 				printf("Auto-adding %s\n",str);
 				addlist(str,f,desc_offset,size_offset);
@@ -1015,7 +1015,7 @@ int main(int argc, char **argv)
 		if(!listgiven && !namegiven) {
 			sprintf(str,"%s%s.lst",scfg.dir[f.dir]->path, scfg.dir[f.dir]->code);
 			if(!fexistcase(str) || flength(str)<=0L)
-				sprintf(str,"%s%s",scfg.dir[f.dir]->path, auto_name);
+				SAFEPRINTF2(str,"%s%s",scfg.dir[f.dir]->path, auto_name);
 			addlist(str,f,desc_offset,size_offset);
 			if(mode&SYNC_LIST)
 				synclist(str,f.dir);
diff --git a/src/sbbs3/allusers.c b/src/sbbs3/allusers.c
index 641f75e8ff..4d5ea513c7 100644
--- a/src/sbbs3/allusers.c
+++ b/src/sbbs3/allusers.c
@@ -223,7 +223,7 @@ int main(int argc, char **argv)
 					for(;argv[i][j];j++)
 						if(isalpha(argv[i][j]))
 							flags|=FLAG(toupper(argv[i][j]));
-					sprintf(str,"%suser.dat",dir);
+					SAFEPRINTF(str,"%suser.dat",dir);
 					if(!fexistcase(str) || (file=sopen(str,O_RDWR|O_BINARY,SH_DENYNO))==-1) {
 						printf("Error opening %s\n",str);
 						exit(1); 
@@ -287,7 +287,7 @@ int main(int argc, char **argv)
 					for(;argv[i][j];j++)
 						if(isalpha(argv[i][j]))
 							flags|=FLAG(toupper(argv[i][j]));
-					sprintf(str,"%suser.dat",dir);
+					SAFEPRINTF(str,"%suser.dat",dir);
 					if(!fexistcase(str) || (file=sopen(str,O_RDWR|O_BINARY,SH_DENYNO))==-1) {
 						printf("Error opening %s\n",str);
 						exit(1); 
@@ -342,7 +342,7 @@ int main(int argc, char **argv)
 						j=99;
 					if(j<0)
 						j=0;
-					sprintf(str,"%suser.dat",dir);
+					SAFEPRINTF(str,"%suser.dat",dir);
 					if(!fexistcase(str) || (file=sopen(str,O_RDWR|O_BINARY,SH_DENYNO))==-1) {
 						printf("Error opening %s\n",str);
 						exit(1); 
diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp
index ce9ed96bbc..e1d767c597 100644
--- a/src/sbbs3/chat.cpp
+++ b/src/sbbs3/chat.cpp
@@ -1436,7 +1436,7 @@ void sbbs_t::nodemsg()
 					bprintf("%4s",nulstr);
 					if(!getstr(line,70,K_WRAP|K_MSG))
 						break;
-					sprintf(str,"%4s%s\r\n",nulstr,line);
+					SAFEPRINTF2(str,"%4s%s\r\n",nulstr,line);
 					SAFECAT(buf,str);
 					if(line[0]) {
 						if(i)
diff --git a/src/sbbs3/delfiles.c b/src/sbbs3/delfiles.c
index fd42ce04f0..0aa828d7e5 100644
--- a/src/sbbs3/delfiles.c
+++ b/src/sbbs3/delfiles.c
@@ -205,7 +205,7 @@ int main(int argc, char **argv)
 
 		if(misc&NO_LINK && cfg.dir[i]->misc&DIR_FCHK) {
 			strcpy(tmp,cfg.dir[i]->path);
-			sprintf(str,"%s*.*",tmp);
+			SAFEPRINTF(str,"%s*.*",tmp);
 			printf("\nSearching %s for unlinked files\n",str);
 			if(!glob(str, GLOB_MARK, NULL, &gl)) {
 				for(j=0; j<(int)gl.gl_pathc; j++) {
@@ -266,7 +266,7 @@ int main(int argc, char **argv)
 			strcpy(workfile.name,fname);
 			unpadfname(workfile.name,fname);
 			workfile.dir=i;
-			sprintf(str,"%s%s"
+			SAFEPRINTF2(str,"%s%s"
 				,workfile.altpath>0 && workfile.altpath<=cfg.altpaths
 					? cfg.altpath[workfile.altpath-1]
 				: cfg.dir[workfile.dir]->path,fname);
diff --git a/src/sbbs3/dstsedit.c b/src/sbbs3/dstsedit.c
index 48cd5afcce..18fc8a2785 100644
--- a/src/sbbs3/dstsedit.c
+++ b/src/sbbs3/dstsedit.c
@@ -63,7 +63,7 @@ main(int argc, char **argv)
 	}
 	backslash(path);
 
-	sprintf(str, "%sdsts.dab", path);
+	SAFEPRINTF(str, "%sdsts.dab", path);
 	if ((file = nopen(str, O_RDONLY)) == -1) {
 		printf("Can't open %s\r\n", str);
 		exit(1);
@@ -179,7 +179,7 @@ main(int argc, char **argv)
 				stats.nusers = atoi(str);
 			break;
 		case 'Q':
-			sprintf(str, "%sdsts.dab", path);
+			SAFEPRINTF(str, "%sdsts.dab", path);
 			if ((file = nopen(str, O_WRONLY)) == -1) {
 				printf("Error opening %s\r\n", str);
 				exit(1);
diff --git a/src/sbbs3/fido.cpp b/src/sbbs3/fido.cpp
index 3855f05318..f2378f3c1f 100644
--- a/src/sbbs3/fido.cpp
+++ b/src/sbbs3/fido.cpp
@@ -82,7 +82,7 @@ bool sbbs_t::lookup_netuser(char *into)
 		strupr(name);
 		str[35]=0;
 		truncsp(str+27);
-		sprintf(q,"Do you mean %s @%s",str,str+27);
+		SAFEPRINTF2(q,"Do you mean %s @%s",str,str+27);
 		if(strstr(name,to) && yesno(q)) {
 			fclose(stream);
 			sprintf(into,"%s@%s",str,str+27);
diff --git a/src/sbbs3/filelist.c b/src/sbbs3/filelist.c
index 9d963188a8..dfed141029 100644
--- a/src/sbbs3/filelist.c
+++ b/src/sbbs3/filelist.c
@@ -414,7 +414,7 @@ int main(int argc, char **argv)
 			}
 
 			if(misc&MINUS) {
-				sprintf(str,"%s%s",scfg.dir[i]->path,fname);
+				SAFEPRINTF2(str,"%s%s",scfg.dir[i]->path,fname);
 				if(!fexistcase(str))
 					fputc('-',out);
 				else
@@ -425,7 +425,7 @@ int main(int argc, char **argv)
 			desc_off++;
 
 			if(misc&DFD) {
-				sprintf(str,"%s%s",scfg.dir[i]->path,fname);
+				SAFEPRINTF2(str,"%s%s",scfg.dir[i]->path,fname);
 				fprintf(out,"%s ",unixtodstr(&scfg,(time32_t)fdate(str),str));
 				desc_off+=9; 
 			}
diff --git a/src/sbbs3/fmsgdump.c b/src/sbbs3/fmsgdump.c
index 09b7b7170f..b1d2a93565 100644
--- a/src/sbbs3/fmsgdump.c
+++ b/src/sbbs3/fmsgdump.c
@@ -51,7 +51,7 @@ const char* fmsgattr_str(uint16_t attr)
 	if(str[0] == 0)
 		return "";
 
-	static char buf[64];
+	static char buf[128];
 	sprintf(buf, "(%s)", str);
 	return buf;
 }
diff --git a/src/sbbs3/js_bbs.cpp b/src/sbbs3/js_bbs.cpp
index b6786fc82f..0e1121e74b 100644
--- a/src/sbbs3/js_bbs.cpp
+++ b/src/sbbs3/js_bbs.cpp
@@ -3339,7 +3339,7 @@ js_put_telegram(JSContext *cx, uintN argc, jsval *arglist)
 			sbbs->bputs("\1n: \1h");
 			if(!sbbs->getstr(line, 70, i < 4 ? (K_WRAP|K_MSG) : (K_MSG)))
 				break;
-			sprintf(str,"%4s%s\r\n",nulstr,line);
+			SAFEPRINTF2(str,"%4s%s\r\n",nulstr,line);
 			SAFECAT(buf, str);
 			if(i && line[0])
 				SAFECAT(logbuf, " ");
diff --git a/src/sbbs3/listfile.cpp b/src/sbbs3/listfile.cpp
index 241a8d0461..58923be8ea 100644
--- a/src/sbbs3/listfile.cpp
+++ b/src/sbbs3/listfile.cpp
@@ -1164,8 +1164,8 @@ int sbbs_t::listfileinfo(uint dirnum, char *filespec, long mode)
 								&& findfile(&cfg,f.dir,path))
 								bprintf(text[FileAlreadyThere],path);
 							else {
-								sprintf(path,"%s%s",dirpath,fname);
-								sprintf(tmp,"%s%s",dirpath,str);
+								SAFEPRINTF2(path,"%s%s",dirpath,fname);
+								SAFEPRINTF2(tmp,"%s%s",dirpath,str);
 								if(fexistcase(path) && rename(path,tmp))
 									bprintf(text[CouldntRenameFile],path,tmp);
 								else {
@@ -1226,7 +1226,7 @@ int sbbs_t::listfileinfo(uint dirnum, char *filespec, long mode)
 					update_uldate(&cfg, &f);
 					break;
 				case 'F':   /* delete file only */
-					sprintf(str,"%s%s",dirpath,fname);
+					SAFEPRINTF2(str,"%s%s",dirpath,fname);
 					if(!fexistcase(str))
 						bprintf(text[FileDoesNotExist],str);
 					else {
@@ -1245,7 +1245,7 @@ int sbbs_t::listfileinfo(uint dirnum, char *filespec, long mode)
 					if(noyes(text[RemoveFileQ]))
 						break;
 					removefile(&f);
-					sprintf(str,"%s%s",dirpath,fname);
+					SAFEPRINTF2(str,"%s%s",dirpath,fname);
 					if(fexistcase(str)) {
 						if(dir_op(dirnum)) {
 							if(!noyes(text[DeleteFileQ])) {
@@ -1340,7 +1340,7 @@ int sbbs_t::listfileinfo(uint dirnum, char *filespec, long mode)
 					break; } 
 		}
 		else if(mode==FI_DOWNLOAD || mode==FI_USERXFER) {
-			sprintf(path,"%s%s",dirpath,fname);
+			SAFEPRINTF2(path,"%s%s",dirpath,fname);
 			if(f.size<1L) { /* getfiledat will set this to -1 if non-existant */
 				SYNC;       /* and 0 byte files shouldn't be d/led */
 				mnemonics(text[QuitOrNext]);
@@ -1417,8 +1417,8 @@ int sbbs_t::listfileinfo(uint dirnum, char *filespec, long mode)
 							lncntr=0;
 							seqwait(cfg.dir[f.dir]->seqdev);
 							bprintf(text[RetrievingFile],fname);
-							sprintf(str,"%s%s",dirpath,fname);
-							sprintf(path,"%s%s",cfg.temp_dir,fname);
+							SAFEPRINTF2(str,"%s%s",dirpath,fname);
+							SAFEPRINTF2(path,"%s%s",cfg.temp_dir,fname);
 							mv(str,path,1); /* copy the file to temp dir */
 							if(getnodedat(cfg.node_num,&thisnode,true)==0) {
 								thisnode.aux=0xf0;
diff --git a/src/sbbs3/netmail.cpp b/src/sbbs3/netmail.cpp
index 65dae51a6b..5e6f206c2c 100644
--- a/src/sbbs3/netmail.cpp
+++ b/src/sbbs3/netmail.cpp
@@ -204,7 +204,7 @@ bool sbbs_t::netmail(const char *into, const char *title, long mode, smb_t* resm
 		SAFECOPY(tmp, cfg.data_dir);
 		if(tmp[0]=='.')    /* Relative path */
 			sprintf(tmp,"%s%s", cfg.node_dir, cfg.data_dir);
-		sprintf(str,"%sfile/%04u.out/%s",tmp,useron.number,fname);
+		SAFEPRINTF3(str,"%sfile/%04u.out/%s",tmp,useron.number,fname);
 		SAFECOPY(subj, str);
 		if(fexistcase(str)) {
 			bputs(text[FileAlreadyThere]);
@@ -1423,7 +1423,7 @@ bool sbbs_t::qnetmail(const char *into, const char *subj, long mode, smb_t* resm
 	useron.etoday++;
 	putuserrec(&cfg,useron.number,U_ETODAY,5,ultoa(useron.etoday,tmp,10));
 
-	sprintf(str,"sent QWK NetMail to %s (%s)"
+	SAFEPRINTF2(str,"sent QWK NetMail to %s (%s)"
 		,to,fulladdr);
 	logline("EN",str);
 	return(true);
diff --git a/src/sbbs3/qwknodes.c b/src/sbbs3/qwknodes.c
index 46c1e976b8..de128146e4 100644
--- a/src/sbbs3/qwknodes.c
+++ b/src/sbbs3/qwknodes.c
@@ -340,7 +340,7 @@ int main(int argc, char **argv)
 								p=addr;
 							else
 								*(p++)=0;
-							sprintf(str,"%s %s:%s%c%s"
+							safe_snprintf(str, sizeof(str), "%s %s:%s%c%s"
 								,unixtodstr(&cfg,msg.hdr.when_written.time,tmp)
 								,p,cfg.sys_id,p==addr ? 0 : '/'
 								,addr);
diff --git a/src/sbbs3/sbbscon.c b/src/sbbs3/sbbscon.c
index 8dba45eaab..c603e9f933 100644
--- a/src/sbbs3/sbbscon.c
+++ b/src/sbbs3/sbbscon.c
@@ -713,7 +713,7 @@ static int bbs_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%sterm %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%sterm %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
@@ -778,7 +778,7 @@ static int stat_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%sstat %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%sstat %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
@@ -845,7 +845,7 @@ static int ftp_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%sftp  %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%sftp  %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
@@ -911,7 +911,7 @@ static int mail_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%smail %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%smail %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
@@ -977,7 +977,7 @@ static int services_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%ssrvc %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%ssrvc %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
@@ -1043,7 +1043,7 @@ static int event_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%sevnt %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%sevnt %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
@@ -1085,7 +1085,7 @@ static int web_lputs(void* p, int level, const char *str)
 			,tm.tm_mon+1,tm.tm_mday
 			,tm.tm_hour,tm.tm_min,tm.tm_sec);
 
-	sprintf(logline,"%sweb  %.*s",tstr,(int)sizeof(logline)-32,str);
+	sprintf(logline,"%sweb  %.*s",tstr,(int)sizeof(logline)-70,str);
 	truncsp(logline);
 	lputs(level,logline);
 
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index f09ba8ee17..08f672e0e4 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -1128,9 +1128,9 @@ static void js_service_thread(void* arg)
 	/* RUN SCRIPT */
 	SAFECOPY(fname,service->cmd);
 	truncstr(fname," ");
-	sprintf(spath,"%s%s",scfg.mods_dir,fname);
+	SAFEPRINTF2(spath,"%s%s",scfg.mods_dir,fname);
 	if(scfg.mods_dir[0]==0 || !fexist(spath))
-		sprintf(spath,"%s%s",scfg.exec_dir,fname);
+		SAFEPRINTF2(spath,"%s%s",scfg.exec_dir,fname);
 
 	js_init_args(js_cx, js_glob, service->cmd);
 
@@ -1246,9 +1246,9 @@ static void js_static_service_thread(void* arg)
 
 	SAFECOPY(fname,service->cmd);
 	truncstr(fname," ");
-	sprintf(spath,"%s%s",scfg.mods_dir,fname);
+	SAFEPRINTF2(spath,"%s%s",scfg.mods_dir,fname);
 	if(scfg.mods_dir[0]==0 || !fexist(spath))
-		sprintf(spath,"%s%s",scfg.exec_dir,fname);
+		SAFEPRINTF2(spath,"%s%s",scfg.exec_dir,fname);
 
 	do {
 		if((js_cx=js_initcx(js_runtime,INVALID_SOCKET,&service_client,&js_glob))==NULL) {
diff --git a/src/sbbs3/slog.c b/src/sbbs3/slog.c
index 2e2ce40df3..065bee676f 100644
--- a/src/sbbs3/slog.c
+++ b/src/sbbs3/slog.c
@@ -54,7 +54,7 @@ if(!dir[0]) {
 
 backslash(dir);
 
-sprintf(str,"%scsts.dab",dir);
+SAFEPRINTF(str,"%scsts.dab",dir);
 if(!fexistcase(str)) {
 	printf("%s does not exist\r\n",str);
 	return(1); }
diff --git a/src/sbbs3/un_qwk.cpp b/src/sbbs3/un_qwk.cpp
index ac856bdbda..9b0472baf3 100644
--- a/src/sbbs3/un_qwk.cpp
+++ b/src/sbbs3/un_qwk.cpp
@@ -396,7 +396,7 @@ bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
 		MKDIR(inbox);
 
 		// Copy files
-		sprintf(fname,"%s/%s",inbox,dirent->d_name);
+		SAFEPRINTF2(fname,"%s/%s",inbox,dirent->d_name);
 		mv(str,fname,1 /* overwrite */);
 		sprintf(str,text[ReceivedFileViaQWK],dirent->d_name,cfg.qhub[hubnum]->id);
 		putsmsg(&cfg,1,str);
diff --git a/src/sbbs3/upload.cpp b/src/sbbs3/upload.cpp
index 05476e6db1..4733ad1cab 100644
--- a/src/sbbs3/upload.cpp
+++ b/src/sbbs3/upload.cpp
@@ -316,7 +316,7 @@ bool sbbs_t::upload(uint dirnum)
 			,cfg.dir[dirnum]->sname);
 	if(!yesno(str)) return(false);
 	action=NODE_ULNG;
-	sprintf(str,"%s%s",path,fname);
+	SAFEPRINTF2(str,"%s%s",path,fname);
 	if(fexistcase(str)) {   /* File is on disk */
 #ifdef _WIN32
 		GetShortPathName(str, spath, sizeof(spath));
@@ -459,7 +459,7 @@ bool sbbs_t::upload(uint dirnum)
 		if(!noyes(text[AnonymousQ]))
 			f.misc|=FM_ANON; 
 	}
-	sprintf(str,"%s%s",path,fname);
+	SAFEPRINTF2(str,"%s%s",path,fname);
 	if(fexistcase(str)) {   /* File is on disk */
 		if(!uploadfile(&f))
 			return(false); 
@@ -570,7 +570,7 @@ bool sbbs_t::bulkupload(uint dirnum)
 	SYNC;
 	dir=opendir(path);
 	while(dir!=NULL && (dirent=readdir(dir))!=NULL && !msgabort()) {
-		sprintf(str,"%s%s",path,dirent->d_name);
+		SAFEPRINTF2(str,"%s%s",path,dirent->d_name);
 		if(isdir(str))
 			continue;
 #ifdef _WIN32
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 0a7223450d..e88d3741aa 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -1518,18 +1518,19 @@ static void send_error(http_session_t * session, unsigned line, const char* mess
 
 		if(session->req.error_dir) {
 			/* We have a custom error directory from webctrl.ini look there first */
-			sprintf(sbuf,"%s%s%s",session->req.error_dir,error_code,startup->ssjs_ext);
+			SAFEPRINTF3(sbuf,"%s%s%s",session->req.error_dir,error_code,startup->ssjs_ext);
 			if(stat(sbuf,&sb)) {
 				/* No custom .ssjs error message... check for custom .html */
-				sprintf(sbuf2,"%s%s.html",session->req.error_dir,error_code);
+				SAFEPRINTF2(sbuf2,"%s%s.html",session->req.error_dir,error_code);
 				if(stat(sbuf2,&sb)) {
 					/* Nope, no custom .html error either, check for global ssjs one */
-					sprintf(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
+					SAFEPRINTF3(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
 				}
 			}
 		}
-		else
-			sprintf(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
+		else {
+			SAFEPRINTF3(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
+		}
 		if(!stat(sbuf,&sb)) {
 			lprintf(LOG_INFO,"%04d Using SSJS error page",session->socket);
 			session->req.dynamic=IS_SSJS;
@@ -3611,7 +3612,7 @@ static BOOL check_request(http_session_t * session)
 		p=last_slash;
 		/* Terminate the path after the slash */
 		*(last_slash+1)=0;
-		sprintf(str,"%saccess.ars",curdir);
+		SAFEPRINTF(str,"%saccess.ars",curdir);
 		if(!stat(str,&sb)) {
 			/* NEVER serve up an access.ars file */
 			lprintf(LOG_WARNING,"%04d !WARNING! access.ars support is deprecated and will be REMOVED very soon.",session->socket);
@@ -3633,7 +3634,7 @@ static BOOL check_request(http_session_t * session)
 			/* Truncate at \r or \n - can use last_slash since I'm done with it.*/
 			truncsp(session->req.ars);
 		}
-		sprintf(str,"%swebctrl.ini",curdir);
+		SAFEPRINTF(str,"%swebctrl.ini",curdir);
 		if(!stat(str,&sb)) {
 			/* NEVER serve up a webctrl.ini file */
 			if(!strcmp(path,str)) {
diff --git a/src/sbbs3/xtrn_sec.cpp b/src/sbbs3/xtrn_sec.cpp
index 473f801190..0297e571ab 100644
--- a/src/sbbs3/xtrn_sec.cpp
+++ b/src/sbbs3/xtrn_sec.cpp
@@ -212,7 +212,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 			return; 
 		}
 
-		sprintf(str,"%s\n%s\n%s\n%s\n"
+		safe_snprintf(str, sizeof(str), "%s\n%s\n%s\n%s\n"
 			,name								/* User name */
 			,cfg.sys_name						/* System name */
 			,cfg.sys_op 						/* Sysop name */
@@ -220,7 +220,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%s\n%s\n%u\n%u\n%lu\n%s\n%lu\n%lu\n"
+		safe_snprintf(str, sizeof(str), "%s\n%s\n%u\n%u\n%lu\n%s\n%lu\n%lu\n"
 			,ctrl_dir							/* Ctrl dir */
 			,data_dir							/* Data dir */
 			,cfg.sys_nodes						/* Total system nodes */
@@ -234,7 +234,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%u\n%u\n%s\n%c\n%u\n%s\n"
+		safe_snprintf(str, sizeof(str), "%u\n%u\n%s\n%c\n%u\n%s\n"
 			,useron.level						/* User main level */
 			,useron.level						/* User transfer level */
 			,useron.birth						/* User birthday */
@@ -244,7 +244,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%u\n%u\n%x\n%lu\n%s\n%s\n"
+		safe_snprintf(str, sizeof(str), "%u\n%u\n%x\n%lu\n%s\n%s\n"
 			"%s\n%s\n%s\n%s\n%s\n%s\n%lu\n"
 			,misc&(XTRN_STDIO|XTRN_CONIO) ? 0:cfg.com_port		/* Com port or 0 if !FOSSIL */
 			,cfg.com_irq						/* Com IRQ */
@@ -295,7 +295,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%s\n%s\n%d\n%s\n%lu\n%s\n%s\n%s\n%s\n"
+		safe_snprintf(str, sizeof(str), "%s\n%s\n%d\n%s\n%lu\n%s\n%s\n%s\n%s\n"
 			"%" PRIx32 "\n%d\n"
 			,ltoaf(useron.flags3,tmp)			/* Flag set #3 */
 			,ltoaf(useron.flags4,tmp2)			/* Flag set #4 */
@@ -329,7 +329,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		if(tleft>0x7fff)	/* Reduce time-left for broken 16-bit doors		*/
 			tleft=0x7fff;	/* That interpret this value as a signed short	*/
 
-		sprintf(str,"%u\n%s\n%s\n%s\n%u\n%c\n"
+		safe_snprintf(str, sizeof(str), "%u\n%s\n%s\n%s\n%u\n%c\n"
 			,useron.number						/* User number */
 			,name								/* User name */
 			,useron.name						/* User real name */
@@ -340,7 +340,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%lu\n%s\n%lu\n%ld\n%u\n%u\n%u\n%ld\n%u\n"
+		safe_snprintf(str, sizeof(str), "%lu\n%s\n%lu\n%ld\n%u\n%u\n%u\n%ld\n%u\n"
 			,useron.cdt+useron.freecdt			/* Gold */
 			,unixtodstr(&cfg,useron.laston,tmp)	/* User last on date */
 			,cols 								/* User screen width */
@@ -353,7 +353,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%lu\n%s\n%s\n%s\n%lu\n%d\n%s\n%s\n"
+		safe_snprintf(str, sizeof(str), "%lu\n%s\n%s\n%s\n%lu\n%d\n%s\n%s\n"
 			"%u\n%u\n%lu\n%u\n%lu\n%u\n%s\n"
 			,tleft								/* Time left in seconds */
 			,node_dir							/* Gfiles dir (log dir) */
@@ -464,7 +464,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 		lfexpand(str,misc);
 		write(file,str,strlen(str));
 
-		sprintf(str,"%u\n%lu\n%s\n%s\n%s\n%s"
+		safe_snprintf(str, sizeof(str), "%u\n%lu\n%s\n%s\n%s\n%s"
 			"\n%s\n%02d:%02d\n%c\n"
 			,0									/* 30: Kbytes downloaded today */
 			,(useron.cdt+useron.freecdt)/1024UL /* 31: Max Kbytes to download today */
-- 
GitLab


From aa7bd2bf86816e143c28be7b9720d68f7376105d Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 01:19:20 -0700
Subject: [PATCH 725/752] Ignore the root/.well-known dir

Where the LetSyncrypt challenge-response file is placed.
---
 webv4/.gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/webv4/.gitignore b/webv4/.gitignore
index e120441f21..884830444d 100644
--- a/webv4/.gitignore
+++ b/webv4/.gitignore
@@ -15,4 +15,5 @@ components/*
 
 root/css/custom.css
 root/logo.gif
+root/.well-known
 
-- 
GitLab


From f4e01eac66ccb8126d3036f309c1ee45fa40bc94 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 01:29:24 -0700
Subject: [PATCH 726/752] Removed the minimum_password_length setting, now
 obsolete

---
 ctrl/modopts.ini | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/ctrl/modopts.ini b/ctrl/modopts.ini
index afecc5e204..17858a2b0a 100644
--- a/ctrl/modopts.ini
+++ b/ctrl/modopts.ini
@@ -45,14 +45,14 @@
 	fast_logon = true
 	fast_logon_requirements =
         eval_first =
-        eval_last = 
-        
-[logonlist]        
+        eval_last =
+
+[logonlist]
         last_few_callers = 4
         last_few_callers_msg =
         last_few_callers_fmt =
-        first_caller_msg = 
-        logons_header_fmt = 
+        first_caller_msg =
+        logons_header_fmt =
         nobody_logged_on_fmt =
         today =
         yesterday =
@@ -78,10 +78,10 @@
 
 [flashpolicyserver]
 ; If you want to allow flash clients to connect to additional ports, add them here as a comma
-; separated list (e.g. 21,25).  
+; separated list (e.g. 21,25).
 ; NOTE: Telnet, RLogin and SSH ports are already allowed, so do not need to be listed here
 	extra_ports=
-	
+
 [jsonchat]
 ;see exec/load/json-server.js for help setting up a chat server
 	host = bbs.thebrokenbubble.com
@@ -103,7 +103,7 @@
     include_web_users = true
     web_inactivity_timeout = 900
     web_browsing = browsing
-    
+
 ; advanced nodelist customizations:
     ;format: \1n\1h%3d  \1n\1g%s
     ;username_prefix: \1h
@@ -113,7 +113,7 @@
     ;connection_prefix:
     ;errors_prefix: \1h\1r
     ;gender_separator = " "
-    
+
 [sbbslist]
 	export_freq = 7
 	add_ars = not guest
@@ -127,7 +127,6 @@
     timeout=43200
     inactivity=900
     user_registration=true
-    minimum_password_length=6
     maximum_telegram_length=800
     web_directory=../webv4
     ftelnet=true
-- 
GitLab


From 07b2cc249e7cc553aa81f375c9c23c7956d8fb65 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 01:32:09 -0700
Subject: [PATCH 727/752] Mention the minimum password length setting

---
 docs/v318_new.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/v318_new.txt b/docs/v318_new.txt
index e4f1738430..42c800c4e8 100644
--- a/docs/v318_new.txt
+++ b/docs/v318_new.txt
@@ -81,6 +81,7 @@ Security
 ~~~~~~~~
 o User security notifications: failed login attempts, password requests
 o User password maximum length increased from 8 to 40 characters
+o Sysop can set the minimum user password length from 4 to 40 characters
 o Sysop-reset of the failed-login/temp-ban list via "ctrl/clear" sem file
 o Updated TLS/SSL/SSH library (Cryptlib 3.4.5) and patched for compatibility
   and security
-- 
GitLab


From dc641557e8eb173ead2db6d7203e96f05b117e15 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 15:42:34 -0700
Subject: [PATCH 728/752] Fix a couple of infinite loops observed when user
 disconnected

---
 exec/sbbslist.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/exec/sbbslist.js b/exec/sbbslist.js
index 5e96ca6bca..4e12a07731 100644
--- a/exec/sbbslist.js
+++ b/exec/sbbslist.js
@@ -1082,7 +1082,7 @@ function browse(list)
 	var num_entries_on_page = 0;
 	var prompt_row = 0;
 	var previous_prompt_row = 0;
-	while(!js.terminated) {
+	while(js.global.bbs.online && !js.terminated) {
 //		console.clear(LIGHTGRAY);
 		console.home();
 		console.current_column = 0;
@@ -1590,7 +1590,7 @@ function view(list, current)
 {
 	console.line_counter = 0;
 	console.clear(LIGHTGRAY);
-	while(!js.terminated) {
+	while(js.global.bbs.online && !js.terminated) {
 
 		/* Bounds checking: */
 		if(current < 0) {
-- 
GitLab


From fe63d4ce0e51ecaa6521d771ee243672a3af4230 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 19:48:49 -0700
Subject: [PATCH 729/752] Use the passthru socket connection for node-spy

This enables node-spying via SSH or spying on a node that is using SSH.
Thanks Altere for the bug report.
---
 src/sbbs3/str.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp
index 6c6f4e2424..839d72df79 100644
--- a/src/sbbs3/str.cpp
+++ b/src/sbbs3/str.cpp
@@ -1077,7 +1077,10 @@ bool sbbs_t::spy(uint i /* node_num */)
 	}
 	bprintf("*** Synchronet Remote Spy on Node %d: Ctrl-C to Abort ***"
 		"\r\n\r\n",i);
-	spy_socket[i-1]=client_socket;
+	if(passthru_thread_running)
+		spy_socket[i-1]=client_socket_dup;
+	else
+		spy_socket[i-1]=client_socket;
 	ansi_len=0;
 	while(online 
 		&& client_socket!=INVALID_SOCKET 
@@ -1120,7 +1123,7 @@ bool sbbs_t::spy(uint i /* node_num */)
 			lncntr=0;						/* defeat pause */
 			spy_socket[i-1]=INVALID_SOCKET;	/* disable spy output */
 			ch=handle_ctrlkey(ch,K_NONE);
-			spy_socket[i-1]=client_socket;	/* enable spy output */
+			spy_socket[i-1] = passthru_thread_running ? client_socket_dup : client_socket;	/* enable spy output */
 			if(ch==0)
 				continue;
 		}
@@ -1128,6 +1131,7 @@ bool sbbs_t::spy(uint i /* node_num */)
 			RingBufWrite(node_inbuf[i-1],(uchar*)&ch,1);
 	}
 	spy_socket[i-1]=INVALID_SOCKET;
+
 	return(true);
 }
 
-- 
GitLab


From a70713b3a0e6b50957517ab23ae533d8f94e5d63 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 22:26:08 -0700
Subject: [PATCH 730/752] Improve the ANSI sequence
 parsing/stripping/pass-through in spy()

Strip all *but* the expected keyboard input sequences (arrow keys, pgup/dn,
home, end, insert). Pass-through a bare ESC key (albeit with delay). ESC
immediately followed by a key other than '[' should be very quickly passed
through.
Increase the max ANSI sequence length from 31 to 256 chars.
---
 src/sbbs3/str.cpp | 60 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 38 insertions(+), 22 deletions(-)

diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp
index 839d72df79..c3732489c0 100644
--- a/src/sbbs3/str.cpp
+++ b/src/sbbs3/str.cpp
@@ -1059,8 +1059,8 @@ extern RingBuf* node_inbuf[];
 bool sbbs_t::spy(uint i /* node_num */)
 {
 	char	ch;
-	char	ansi_seq[32];
-	int		ansi_len;
+	char	ansi_seq[256];
+	size_t	ansi_len;
 	int		in;
 
 	if(!i || i>MAX_NODES) {
@@ -1092,31 +1092,47 @@ bool sbbs_t::spy(uint i /* node_num */)
 			continue;
 		}
 		ch=in;
-		if(ch==ESC) {
-			if(!ansi_len) {
-				ansi_seq[ansi_len++]=ch;
-				continue;
+		if(ch == ESC) {
+			if(ansi_len)
+				ansi_len = 0;
+			else {
+				if((in = incom(500)) != NOINP) {
+					if(in == '[') {
+						ansi_seq[ansi_len++] = ESC;
+						ansi_seq[ansi_len++] = '[';
+						continue;
+					} else {
+						if(node_inbuf[i-1] != NULL) {
+							RingBufWrite(node_inbuf[i-1], (uchar*)&ch, sizeof(ch));
+							ch = in;
+						}
+					}
+				}
 			}
-			ansi_len=0;
 		}
-		if(ansi_len && ansi_len<(int)sizeof(ansi_seq)-2) {
-			if(ansi_len==1) {
-				if(ch=='[') {
-					ansi_seq[ansi_len++]=ch;
-					continue;
+		if(ansi_len) {
+			if(ansi_len < sizeof(ansi_seq))
+				ansi_seq[ansi_len++] = ch;
+			if(ch >= '@' && ch <= '~') {
+				switch(ch) {
+					case 'A':	// Up
+					case 'B':	// Down
+					case 'C':	// Right
+					case 'D':	// Left
+					case 'F':	// Preceding line
+					case 'H':	// Home
+					case 'K':	// End
+					case 'V':	// PageUp
+					case 'U':	// PageDn
+					case '@':	// Insert
+					case '~':	// Various VT-220
+						// Pass-through these sequences to spied-upon node (eat all others)
+						if(node_inbuf[i-1] != NULL) 
+							RingBufWrite(node_inbuf[i-1], (uchar*)ansi_seq, ansi_len);
+						break;
 				}
 				ansi_len=0;
 			}
-			if(ch=='R') { /* throw-away cursor position report */
-				ansi_len=0;
-				continue;
-			}
-			ansi_seq[ansi_len++]=ch;
-			if(isalpha(ch)) {
-				if(node_inbuf[i-1]!=NULL) 
-					RingBufWrite(node_inbuf[i-1],(uchar*)ansi_seq,ansi_len);
-				ansi_len=0;
-			}
 			continue;
 		}
 		if(ch<' ') {
-- 
GitLab


From 475d34d56de5c155280177f1b518f61a37612ec2 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 22:29:47 -0700
Subject: [PATCH 731/752] Don't send telnet commands to spy sockets

send_telnet_cmd() now sends telnet commands/replies directly to the client
socket. This avoid the entire output_thread() teeing of sent data to spy
sockets/buffers (which may not be Telnet at all).

There are some concerns with this change:
- if client_socket isn't writable, will sendsocket() block?
- if output_thread is sending on the same socket, are our 2/3-byte telnet
  commands going to have other output data interleaved within? (seems very
  unlikely)
---
 src/sbbs3/main.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index f1b26382e5..3fd4dbfe8e 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -1768,14 +1768,14 @@ void sbbs_t::send_telnet_cmd(uchar cmd, uchar opt)
             lprintf(LOG_DEBUG,"sending telnet cmd: %s"
                 ,telnet_cmd_desc(cmd));
 		sprintf(buf,"%c%c",TELNET_IAC,cmd);
-		putcom(buf,2);
+		sendsocket(client_socket, buf, 2);
 	} else {
 		if(startup->options&BBS_OPT_DEBUG_TELNET)
 			lprintf(LOG_DEBUG,"sending telnet cmd: %s %s"
 				,telnet_cmd_desc(cmd)
 				,telnet_opt_desc(opt));
 		sprintf(buf,"%c%c%c",TELNET_IAC,cmd,opt);
-		putcom(buf,3);
+		sendsocket(client_socket, buf, 3);
 	}
 }
 
@@ -5840,7 +5840,7 @@ NO_SSH:
 			new_node->client_socket_dup=accept(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len);
 
 			if(new_node->client_socket_dup == INVALID_SOCKET) {
-				lprintf(LOG_ERR,"Node %d !ERROR (%d) connecting accept()ing on passthru socket"
+				lprintf(LOG_ERR,"Node %d !ERROR (%d) accepting on passthru socket"
 					,new_node->cfg.node_num, ERROR_VALUE);
 				lprintf(LOG_WARNING,"Node %d !WARNING native doors which use sockets will not function"
 					,new_node->cfg.node_num);
-- 
GitLab


From f4e89aa0e1a91d4f84bcaebb7eaa126cebd46c40 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sat, 19 Sep 2020 22:50:23 -0700
Subject: [PATCH 732/752] Re-enable the short inactivity timeout for
 non-terminal connections (bots)

As of Oct-25-2018, the NO_EXASCII flag was set in the autoterm variable
when there was no ANSI terminal auto-detected. This defeated the short
inactivity timeout feature of login.js because it was checking specifically
for a zero-value autoterm.

So change this logic to check for no ANSI, PETSCII, or UTF-8 (the 3 indicators
of a valid terminal) - though I suppose PETSCII is questionable (it's not
actually auto-detected, just a non-standard port usually).
---
 exec/login.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/login.js b/exec/login.js
index 4afffb21a7..5c34a90247 100644
--- a/exec/login.js
+++ b/exec/login.js
@@ -24,7 +24,7 @@ var guest = options.guest && system.matchuser("guest");
 
 if(!bbs.online)
 	exit();
-if(!console.autoterm) {
+if(!(console.autoterm&(USER_ANSI | USER_PETSCII | USER_UTF8))) {
 	console.inactivity_hangup = options.inactive_hangup;
 	log(LOG_NOTICE, "terminal not detected, reducing inactivity hang-up timeout to " + console.inactivity_hangup + " seconds");
 }
-- 
GitLab


From bf4be1e1e80f8388ba32d5248a7f3fcfbc55cee7 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 23:03:38 -0700
Subject: [PATCH 733/752] Move the "connect" spy message to where the hostname
 and IP address are known

---
 src/sbbs3/main.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 3fd4dbfe8e..c0502bb5f0 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -3483,8 +3483,6 @@ sbbs_t::sbbs_t(ushort node_num, union xp_sockaddr *addr, size_t addr_len, const
 	/* used by update_qwkroute(): */
 	qwknode=NULL;
 	total_qwknodes=0;
-
-	spymsg("Connected");
 }
 
 //****************************************************************************
@@ -3533,7 +3531,7 @@ bool sbbs_t::init()
 		inet_addrtop(&client_addr, client_ipaddr, sizeof(client_ipaddr));
 		lprintf(LOG_INFO,"socket %u attached to local interface %s port %u"
 			,client_socket, local_addr, inet_addrport(&addr));
-
+		spymsg("Connected");
 	}
 
 	if((comspec=os_cmdshell())==NULL) {
-- 
GitLab


From 2e0e012136da6c54a735bd271f3f77edfdc01564 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 19 Sep 2020 23:21:04 -0700
Subject: [PATCH 734/752] Change the pause prompt to "More (Y/n) ?" and allow
 'N' to stop.

---
 src/sbbs3/slog.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/slog.c b/src/sbbs3/slog.c
index 065bee676f..958cd65629 100644
--- a/src/sbbs3/slog.c
+++ b/src/sbbs3/slog.c
@@ -111,9 +111,10 @@ while(l>-1L) {
 		,fbacks,ulb/1024,uls,dlb/1024,dls);
 	lncntr++;
 	if(pause && lncntr>=20) {
-		printf("[Hit a key]");
+		printf("More (Y/n) ? ");
 		fflush(stdout);
-		if(getchar()==3)
+		char ch = getchar();
+		if(ch == CTRL_C || toupper(ch) == 'N')
 			break;
 		printf("\r");
 		lncntr=0; } }
-- 
GitLab


From 4ae7fe5679498bda7b66ecc811733c9fc2a75d80 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 00:37:56 -0700
Subject: [PATCH 735/752] inkey() now returns TERM_KEY_* constants (left,
 right, up, down, etc.)

Use the same terminal ctrl key values as sbbs.
getstr() might need some love here (especiall left/right arrow).
---
 xtrn/sdk/xsdk.c     | 61 +++++++++++++++++++++++++++++++++++++--------
 xtrn/sdk/xsdkdefs.h | 17 +++++++++++--
 2 files changed, 65 insertions(+), 13 deletions(-)

diff --git a/xtrn/sdk/xsdk.c b/xtrn/sdk/xsdk.c
index d187cefa81..87c06ca157 100644
--- a/xtrn/sdk/xsdk.c
+++ b/xtrn/sdk/xsdk.c
@@ -2,7 +2,7 @@
 
 /* Synchronet External Program Software Development Kit	*/
 
-/* $Id: xsdk.c,v 1.41 2016/08/13 18:57:00 rswindell Exp $ */
+/* $Id: xsdk.c,v 1.41 2020/09/20 18:57:00 rswindell Exp $ */
 
 /****************************************************************************
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
@@ -581,6 +581,7 @@ int keyhit()
 char inkey(long mode)
 {
 	static in_ctrl_p;
+	static ansi_len;
 	uchar ch=0,hour,min,sec;
 	long tleft;
 	int i=0;
@@ -621,19 +622,19 @@ char inkey(long mode)
 			i=stdin_getch();
 			switch(i) {
 				case 0x47:	/* Home - Same as Ctrl-B */
-					return(2);	/* ctrl-b beginning of line */
+					return TERM_KEY_HOME;	/* ctrl-b beginning of line */
 				case 0x4b:		/* Left Arrow - same as ctrl-] */
-					return(0x1d);
+					return TERM_KEY_LEFT;
 				case 0x4d:		/* Right Arrow - same as ctrl-f */
-					return(6);
+					return TERM_KEY_RIGHT;
 				case 0x48:		/* Up arrow - same as ctrl-^ */
-					return(0x1e);
+					return TERM_KEY_UP;
 				case 0x50:		/* Down arrow - same as CR */
-					return(CR);
+					return TERM_KEY_DOWN;
 				case 0x4f:	  /* End	  - same as Ctrl-E */
-					return(5);  /* ctrl-e - end of line */
+					return TERM_KEY_END;
 				case 0x52:	/* Insert */
-					return(0x1f);	/* ctrl-minus - insert mode */
+					return TERM_KEY_INSERT;	/* ctrl-minus - insert mode */
 				case 0x53:	/* Delete */
 					return(0x7f);   /* ctrl-bkspc - del cur char */
 				}
@@ -641,6 +642,44 @@ char inkey(long mode)
 		ch=i;
 	}
 
+	if(ch == ESC) {
+		ansi_len = !ansi_len;
+	}
+	else if(ansi_len == 1) {
+		if(ch == '[')
+			ansi_len++;
+		else
+			ansi_len = 0;
+	}
+	else if(ansi_len == 2) {
+		ansi_len =0 ;
+		switch(ch) {
+			case 'A':
+				return TERM_KEY_UP;
+			case 'B':
+				return TERM_KEY_DOWN;
+			case 'C':
+				return TERM_KEY_RIGHT;
+			case 'D':
+				return TERM_KEY_LEFT;
+			case 'H':
+				return TERM_KEY_HOME;
+			case 'V':
+				return TERM_KEY_PAGEUP;
+			case 'U':
+				return TERM_KEY_PAGEDN;
+			case 'F':
+			case 'K':
+				return TERM_KEY_END;
+			case '@':
+				return TERM_KEY_INSERT;
+			default:
+				return 0;
+		}
+	}
+	if(ansi_len)
+		return 0;
+
 	if(ch==0x10 || ch==0x1e) {	/* Ctrl-P or Ctrl-^ */
 		if(in_ctrl_p || !ctrl_dir[0])	/* keep from being recursive */
 			return(0);
@@ -974,7 +1013,7 @@ int getstr(char *strout, size_t maxlen, long mode)
 				}
 				outchar(str1[i++]=1);
 				break;
-			case 2:	/* Ctrl-B Beginning of Line */
+			case TERM_KEY_HOME:	/* Ctrl-B Beginning of Line */
 				if(user_misc&ANSI && i) {
 					bprintf("\x1b[%dD",i);
 					i=0; 
@@ -1005,7 +1044,7 @@ int getstr(char *strout, size_t maxlen, long mode)
 					l-=x-i; 						/* l=new length */
 				}
 				break;
-			case 5:	/* Ctrl-E End of line */
+			case TERM_KEY_END:	/* Ctrl-E End of line */
 				if(user_misc&ANSI && i<l) {
 					bprintf("\x1b[%dC",l-i);  /* move cursor right one */
 					i=l; 
@@ -1687,7 +1726,7 @@ int nopen(const char *str, int access)
 		bprintf("\r\nNOPEN COLLISION - File: %s Count: %d\r\n"
 			,str,count);
 	if(file==-1 && errno==EACCES)
-		bputs("\7\r\nNOPEN: ACCESS DENIED\r\n\7");
+		bprintf("\7\r\nNOPEN: ACCESS DENIED: %s\r\n\7", str);
 	return(file);
 }
 
diff --git a/xtrn/sdk/xsdkdefs.h b/xtrn/sdk/xsdkdefs.h
index 0f64f43591..f7b91a2109 100644
--- a/xtrn/sdk/xsdkdefs.h
+++ b/xtrn/sdk/xsdkdefs.h
@@ -2,13 +2,13 @@
 
 /* Synchronet XSDK constants, macros, and type definitions */
 
-/* $Id: xsdkdefs.h,v 1.10 2009/01/01 12:45:59 deuce Exp $ */
+/* $Id: xsdkdefs.h,v 1.11 2020/09/20 12:45:59 deuce Exp $ */
 
 /****************************************************************************
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2000 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
  *																			*
  * This library is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU Lesser General Public License		*
@@ -290,6 +290,19 @@ enum {								/* Node Action */
 #define KEY_BUFSIZE 256
 #endif
 
+									/* Special terminal key mappings */
+#define TERM_KEY_HOME	CTRL_B
+#define TERM_KEY_END	CTRL_E
+#define TERM_KEY_UP		CTRL_CARET
+#define TERM_KEY_DOWN	CTRL_J
+#define TERM_KEY_LEFT	CTRL_CLOSE_BRACKET
+#define TERM_KEY_RIGHT	CTRL_F
+#define TERM_KEY_INSERT	CTRL_V
+#define TERM_KEY_DELETE	DEL
+#define TERM_KEY_ABORT	CTRL_C
+#define TERM_KEY_PAGEUP	CTRL_P
+#define TERM_KEY_PAGEDN	CTRL_N
+
 #define CRLF  { outchar(CR); outchar(LF); }
 
 #define SYSOP (user_level>=sysop_level) /* Is current user sysop t/f macro	 */
-- 
GitLab


From a82b69e7cd83f32426371c9c3c31bec362345fc9 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 00:39:13 -0700
Subject: [PATCH 736/752] Support for cursor movement keys (e.g. ANSI) - no
 more NumLock!

---
 xtrn/tbd/tbd.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/xtrn/tbd/tbd.c b/xtrn/tbd/tbd.c
index c593308cfa..cab7b7801c 100644
--- a/xtrn/tbd/tbd.c
+++ b/xtrn/tbd/tbd.c
@@ -370,6 +370,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '4':                           /* Move West */
             case 'A':
+            case TERM_KEY_LEFT:
                 check=inway(x,y,z,gx-1,gy);
                 if(weapon_ready) { tpic=LEFT;
                     if(check)
@@ -404,6 +405,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '6':                           /* Move East */
             case 'D':
+            case TERM_KEY_RIGHT:
                 check=inway(x,y,z,gx+1,gy);
                 if(weapon_ready) { tpic=RIGHT;
                     if(check)
@@ -438,6 +440,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '2':                           /* Move South */
             case 'X':
+            case TERM_KEY_DOWN:
                 check=inway(x,y,z,gx,gy+1);
                 if(weapon_ready) { tpic=DOWN;
                     if(check)
@@ -474,6 +477,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '8':                           /* Move North */
             case 'W':
+            case TERM_KEY_UP:
                 check=inway(x,y,z,gx,gy-1);
                 if(weapon_ready) { tpic=UP;
                     if(check)
@@ -512,6 +516,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '7':                           /* Move NorthWest */
             case 'Q':
+            case TERM_KEY_HOME:
                 check=inway(x,y,z,gx-1,gy-1);
                 if(weapon_ready) { tpic=UP;
                     if(check)
@@ -534,6 +539,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '9':                           /* Move NorthEast */
             case 'E':
+            case TERM_KEY_PAGEUP:
                 check=inway(x,y,z,gx+1,gy-1);
                 if(weapon_ready) { tpic=UP;
                     if(check)
@@ -556,6 +562,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '1':                           /* Move SouthWest */
             case 'Z':
+            case TERM_KEY_END:
                 check=inway(x,y,z,gx-1,gy+1);
                 if(weapon_ready) { tpic=DOWN;
                     if(check)
@@ -578,6 +585,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                 break;
             case '3':                           /* Move SouthEast */
             case 'C':
+            case TERM_KEY_PAGEDN:
                 check=inway(x,y,z,gx+1,gy+1);
                 if(weapon_ready) { tpic=DOWN;
                     if(check)
-- 
GitLab


From 81541de64c48642a12f0d9106409f46721a8f651 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 01:18:05 -0700
Subject: [PATCH 737/752] The text.dat changes between v3.17b and v3.18b

---
 docs/text318b.dif | 321 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 321 insertions(+)
 create mode 100644 docs/text318b.dif

diff --git a/docs/text318b.dif b/docs/text318b.dif
new file mode 100644
index 0000000000..20776574fe
--- /dev/null
+++ b/docs/text318b.dif
@@ -0,0 +1,321 @@
+diff --git a/ctrl/text.dat b/ctrl/text.dat
+index 5a627144f..ae962dbe3 100644
+--- a/ctrl/text.dat
++++ b/ctrl/text.dat
+@@ -1,14 +1,22 @@
+-"\1n\1h\1c�������\1n������\1h\1k�������\1n\1c���������\r\n"\	001 MsgSubj 
+-	"\1h� \1bSubj\1n\1b: \1h\1c%.70s"
+-"\r\n� \1bAttr\1n\1b: \1h\1c%s%s%s%s%s%s%s%s%s%s%s%s%s%s"       002 MsgAttr
+-"\r\n� \1bTo  \1n\1b: \1h\1c%.70s"                              003 MsgTo
+-" #%s"                                                  004 MsgToExt
+-" (%.40s)"                                              005 MsgToNet
+-"\r\n\1w� \1bFrom\1n\1b: \1h\1c%.70s"                         006 MsgFrom
+-" #%s"                                                  007 MsgFromExt
+-" (%.40s)"                                              008 MsgFromNet
+-"\r\n\1w� \1bDate\1n\1b: \1h\1c%.24s %s (%s)\r\n"\      009 MsgDate
+-	"\1w�������\1c���������\1n����������\1h\1k����"\
+-	"����\1n\1c�������\1b�������������\1n"
+-"\1_\r\n\1b\1hE-mail (User name or number): \1w"            010 Email
+-"\r\nPost on %s\1\\ %s"                                     011 Post
++"\1n\1h\1c\xda\xc4\xc4\xc4\xc4\xc4\xc4"\                        001 MsgSubj
++    "\1n\xc4\xc4\xc4\xc4\xc4\xc4"\ 
++    "\1h\1k\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\
++    "\1n\1c\xc4\xc4\xc4\xc4\xc4"\
++    "\xfa\xfa\xfa\xfa"\
++    "\r\n\1h\xb3 \1bSubj\1n\1b: \1h\1c%.70s"       
++"\r\n\xb3 \1bAttr\1n\1b: \1h\1c%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" 002 MsgAttr
++"\r\n\xb3 \1bTo  \1n\1b: \1h\1c%.70s"                           003 MsgTo
++" #%s"                                                          004 MsgToExt
++" (%.40s)"                                                      005 MsgToNet
++"\r\n\1w\xb3 \1bFrom\1n\1b: \1h\1c%.33s"                        006 MsgFrom
++" #%s"                                                          007 MsgFromExt
++" (%.35s)"                                                      008 MsgFromNet
++"\r\n\1w\xb3 \1bDate\1n\1b: \1h\1c%.24s %s (%s)\r\n"\           009 MsgDate
++    "\1w\xc0\xc4\xc4\xc4\xc4\xc4\xc4"\
++    "\1c\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\
++    "\1n\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\
++    "\1h\1k\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\
++    "\1n\1c\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\
++    "\1b\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xfa\xfa\xfa\xfa\xfa\xfa\1n"
++"\1_\1?\1b\1hE-mail (User name or number): \1w"           010 Email
++"\r\nPost on %s\1\\ %s"                                   011 Post
+@@ -17,7 +25,7 @@
+-"\1[\7\1n\1r\1hCannot add \1w%s\1r to queue - \1n"             014 CantAddToQueue
+-"\1n\r\nThe sending of mail is beyond your ability.\r\n" 015 R_Email
+-"\1n\r\nYou can't post messages.\r\n"                    016 R_Post
+-"\1n\r\nYou can't send E-mail to %s.\r\n"                017 R_Feedback
+-"\1n\1m\r\nSending E-mail to \1h%s \1n\1m#%u\1n\r\n"          018 Emailing
+-"\1n\1m\r\nPosting on \1h%s\1n\1m %s\r\n"                    019 Posting
+-"\r\nNew file.\r\n"                                     020 NewFile
++"\1[\7\1n\1r\1hCannot add \1w%s\1r to queue - \1n"        014 CantAddToQueue
++"\1n\r\nThe sending of mail is beyond your ability.\r\n"  015 R_Email
++"\1n\r\nYou can't post messages.\r\n"                     016 R_Post
++"\1n\r\nYou can't send E-mail to %s.\r\n"                 017 R_Feedback
++"\1n\1?\1mSending E-mail to \1h%s \1n\1m#%u\1n\r\n"       018 Emailing
++"\1n\1?\1mPosting on \1h%s\1n\1m %s\r\n"                  019 Posting
++"\r\nNew file.\r\n"                                       020 NewFile
+@@ -29 +37 @@
+-"Line limit (%u) reached.\r\n"                  	024 NoMoreLines
++"\1n\1h\1rLine limit (\1w%u\1r) reached.\1n\r\n"       	024 NoMoreLines
+@@ -35 +43 @@
+-"\r\n\1r\1h\1iAborted.\1n\r\n"                              030 Aborted
++"\1?\1r\1h\1iAborted.\1n\r\n"                              030 Aborted
+@@ -38 +46 @@
+-"\1n\1h\r\nSaving..."                                     033 Saving
++"\1n\1h\r\nSaving @ELLIPSIS@"                           033 Saving
+@@ -40 +48 @@
+-"\1n\r\nWriting Index..."                                035 WritingIndx
++"\1n\r\nWriting Index @ELLIPSIS@"                              035 WritingIndx
+@@ -48 +56 @@
+-"\1b\1hAuto message by: \1c%s\1b on %s\1n\r\n\r\n"           042 AutoMsgBy
++"\1b\1hAuto message by: \1c%s\1b on %s\1n\r\n\r\n"            042 AutoMsgBy
+@@ -53 +61 @@
+-"\1g\1h%4d \1n\1g%-25.25s \1h%c\1n\1g %.46s\r\n"              047 MailWaitingLstFmt
++"\1g\1h%4d \1n\1g%-25.25s \1h%c\1n\1g %s\r\n"              047 MailWaitingLstFmt
+@@ -55,2 +63,2 @@
+-"\r\n\1n\1c� \1b\1hReading E-mail \1n\1c� \1b\1h(\1w?\1b=Menu)"\  049 ReadingMail
+-	" (\1w%u\1b of \1w%u\1b): \1n"
++"\r\n\1n\1c� \1b\1hReading E-mail \1n\1c� \1b\1h(\1w\1`?\1b=Menu)"\  049 ReadingMail
++	" (\1w%u\1b of \1w%u\1b): \1n\1~"
+@@ -73,3 +81,3 @@
+-	"(\1w?\1b=Menu) (\1w%u\1b of \1w%u\1b): \1n"
+-"\1h\1m\r\nSending Internet Mail To: \1w%s\1m\r\n"\         062 InternetMailing
+-	"                    From: \1w%s\r\n"
++	"(\1w\1`?\1b=Menu) (\1w%u\1b of \1w%u\1b): \1n\1~"
++"\1h\1?\1mInternet Mail To: \1w%s\1m\r\n"\         062 InternetMailing
++	"            From: \1w%s\r\n"
+@@ -79 +87 @@
+-"\1_\r\n\1q\1g\1h      From                   "\              065 MailOnSystemLstHdr
++"\1_\r\n\1g\1h      From                   "\              065 MailOnSystemLstHdr
+@@ -81 +89 @@
+-"\1g\1h%5d\1n\1g %-22.22s %-22.22s \1h%c\1n\1g %.25s\r\n"     066 MailOnSystemLstFmt
++"\1g\1h%5d\1n\1g %-22.22s %-22.22s \1h%c\1n\1g %s\r\n"     066 MailOnSystemLstFmt
+@@ -84 +92 @@
+-"\1-\r\n\1c%-15.15s \1y\1h%-44.44s \1w%u"             068 SearchSubFmt
++"\1-\r\n\1c%-20s \1y\1h%-50s \1w%6u"             068 SearchSubFmt
+@@ -88 +96 @@
+-	" %4u\b\b\b\b\1h%s \1n\1c(\1h?\1n\1c=Menu) (\1h%u\1n\1c of \1h%u\1n\1c): \1n"
++	" %4u\b\b\b\b\1h%s \1n\1c(\1h\1`?\1n\1c=Menu) (\1h%u\1n\1c of \1h%u\1n\1c): \1n\1~"
+@@ -90 +98 @@
+-"\r\nDelete message #%u '%s'"                          073 DeletePostQ
++"\1?Delete message #%u '%s'"                          073 DeletePostQ
+@@ -95 +103 @@
+-"\1w\1h�����[\1i\1r%c\1n\1h]���Ĵ "\                    077 PrivateChatSeparator
++"\1w\1h\xc4\xc4\xc4\xc4\xc4[\1i\1r%c\1n\1h]\xc4\xc4\xc4\xc4� "\                    077 PrivateChatSeparator
+@@ -98,3 +106,3 @@
+-	" �����[\1i\1b%c\1n\1h]�����"
+-"\1g\1h%5u\1n\1g %-22.22s %-22.22s \1h%c\1n\1g %.25s\r\n"    078 SubMsgLstFmt
+-"\1w\1h�����[\1i\1r%c\1n\1h]���Ĵ "\                    079 SysopChatSeparator
++	" �\xc4\xc4\xc4\xc4[\1i\1b%c\1n\1h]\xc4\xc4\xc4\xc4\xc4"
++"\1g\1h%5u\1n\1g %-22.22s %-22.22s \1h%c\1n\1g %s\r\n"    078 SubMsgLstFmt
++"\1w\1h\xc4\xc4\xc4\xc4\xc4[\1i\1r%c\1n\1h]\xc4\xc4\xc4\xc4� "\                    079 SysopChatSeparator
+@@ -103 +111 @@
+-	" �����[\1i\1b%c\1n\1h]�����"
++	" �\xc4\xc4\xc4\xc4[\1i\1b%c\1n\1h]\xc4\xc4\xc4\xc4\xc4"
+@@ -112 +120 @@
+-"\1_\r\n\1y\1hBefore which file: \1n"                       088 AddTextFileBeforeWhich
++"\1_\r\n\1y\1hBefore which file [last]: \1n"            088 AddTextFileBeforeWhich
+@@ -146 +154 @@
+-"\r\n\1n\1mSending message to \1h%s\r\n"                121 SendingMessageToUser
++"\1n\1?\1mSending message to \1h%s\r\n"                121 SendingMessageToUser
+@@ -191 +199 @@
+-"\1n\r\n\1mSending telegram to \1h%s #%u\1n\1m\r\n"\    162 SendingTelegramToUser
++"\1n\1?\1mSending telegram to \1h%s #%u\1n\1m\r\n"\    162 SendingTelegramToUser
+@@ -196,2 +204,2 @@
+-"\r\n\1w\1hSearching all directories...\r\n"              166 SearchingAllDirs
+-"\1w\1hSearching all libraries...\r\n"                    167 SearchingAllLibs
++"\r\n\1w\1hSearching all directories @ELLIPSIS@\r\n"      166 SearchingAllDirs
++"\1w\1hSearching all libraries @ELLIPSIS@\r\n"            167 SearchingAllLibs
+@@ -206 +214 @@
+-"\1_\r\n\r\n\1y\1hFilespec: \1n"                            175 FileSpec
++"\1_\r\n\r\n\1y\1hFilespec: \1n\1~"                     175 FileSpec
+@@ -216 +224 @@
+-"\1n\r\nInvalid NetMail address.\r\n"                   183 InvalidNetMailAddr
++"\1n\r\nInvalid NetMail address: \1h%s\1n\r\n"                183 InvalidNetMailAddr
+@@ -218 +226 @@
+-"\1n\1h[\1y%c\1w] \1b%-25s  "                           185 TransferProtLstFmt
++"\1n\1h[\1y\1~%c\1w] \1b%-25s  "                        185 TransferProtLstFmt
+@@ -232 +240 @@
+-"\1r\1hFilenames, specs, or flags [None]: \1m\1h"           197 BatchDlFlags
++"\1r\1hFilenames, specs, or flags [None]: \1m\1h\1~"        197 BatchDlFlags
+@@ -277,3 +285,3 @@
+-	"\1w~Q\1yuit or [Next]: \1w"
+-"\r\n~Quit or [Next]: "                                 233 QuitOrNext
+-"~Remove, ~Move, ~Edit, ~View, ~Quit, or [Next]: "      234 RExemptRemoveFilePrompt
++	"\1w~Q\1yuit or [~Next]: \1w"
++"\r\n~Quit or [~Next]: "                                233 QuitOrNext
++"~Remove, ~Move, ~Edit, ~View, ~Quit, or [~Next]: "      234 RExemptRemoveFilePrompt
+@@ -287,2 +295,2 @@
+-	"~View, ~Quit, or [Next]: "
+-"~Remove, ~Edit, ~View, ~Quit, or [Next]: "             242 UserRemoveFilePrompt
++	"~View, ~Quit, or [~Next]: "
++"~Remove, ~Edit, ~View, ~Quit, or [~Next]: "            242 UserRemoveFilePrompt
+@@ -311 +319 @@
+-"\r\nProtocol, ~Batch, ~Quit, or [Next]: "              264 ProtocolBatchQuitOrNext
++"\r\nProtocol, ~Batch, ~Quit, or [~Next]: "             264 ProtocolBatchQuitOrNext
+@@ -325 +333 @@
+-"\1g\1h%2d: \1n\1g%s     %s\r\n"                            275 UploadQueueLstFmt
++"\1g\1h%2d: \1n\1g%-12s     %s\r\n"                          275 UploadQueueLstFmt
+@@ -328 +336 @@
+-"\1g\1h%2d: \1n\1g%s %11.11s %11.11s     %s\r\n"            277 DownloadQueueLstFmt
++"\1g\1h%2d: \1n\1g%-12s %11.11s %11.11s     %s\r\n"       277 DownloadQueueLstFmt
+@@ -334 +342 @@
+-"\r\nUpload queue is empty\r\n"                         281 UploadQueueIsEmpty
++"\r\nUpload queue is empty.\r\n"                        281 UploadQueueIsEmpty
+@@ -337 +345 @@
+-"\1_\r\n\1b\1hDisconnecting... \1wH\1bang up or \1wA\1bbort "  284 Disconnecting
++"\1_\r\n\1b\1hDisconnecting @ELLIPSIS@ \1wH\1bang up or \1wA\1bbort "  284 Disconnecting
+@@ -340 +348 @@
+-"Deleting files in temp directory...\r\n"               287 RemovingTempFiles
++"Deleting files in temp directory @ELLIPSIS@\r\n"           287 RemovingTempFiles
+@@ -355 +363 @@
+-"\1wSorting..."                                          300 Sorting
++"\1wSorting @ELLIPSIS@"                                   300 Sorting
+@@ -395 +403 @@
+-"\1_\r\n\1y\1hNew password (4-8 chars): "                  332 NewPassword
++"\1_\1?\1y\1hNew password (%u-%u chars): "                  332 NewPasswordPromptFmt
+@@ -401,10 +409,10 @@
+-"\1_\1b\1h[\1c�\1b] \1yEnter your full name or alias\1\\: \1w"     338 EnterYourAlias
+-"\1_\1b\1h[\1c�\1b] \1yEnter your full real name\1\\: \1w"         339 EnterYourRealName
+-"\1_\1b\1h[\1c�\1b] \1yEnter your company name\1\\: \1w"           340 EnterYourCompany
+-"\1_\1b\1h[\1c�\1b] \1yEnter your handle or call-sign\1\\: \1w"    341 EnterYourHandle
+-"\1_\1b\1h[\1c�\1b] \1yEnter your sex (M/F)\1\\: \1w"              342 EnterYourSex
+-"\1_\1b\1h[\1c�\1b] \1yEnter your street address\1\\: \1w"         343 EnterYourAddress
+-"\1_\1b\1h[\1c�\1b] \1yEnter your voice phone number\1\\: \1w"     344 EnterYourPhoneNumber
+-"\1_\1b\1h[\1c�\1b] \1yEnter your birthday (%s)\1\\: \1w"          345 EnterYourBirthday
+-"\1_\1b\1h[\1c�\1b] \1yEnter your location\1\\ (e.g. city, state)\1\\: \1w" 346 EnterYourCityState
+-"\1_\1b\1h[\1c�\1b] \1yEnter your zip (or postal) code\1\\: \1w"   347 EnterYourZipCode
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your full name or alias\1\\: \1w"     338 EnterYourAlias
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your full real name\1\\: \1w"         339 EnterYourRealName
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your company name\1\\: \1w"           340 EnterYourCompany
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your handle or call-sign\1\\: \1w"    341 EnterYourHandle
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your sex (M/F)\1\\: \1w"              342 EnterYourSex
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your street address\1\\: \1w"         343 EnterYourAddress
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your voice phone number\1\\: \1w"     344 EnterYourPhoneNumber
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your birthday (@DATEFMT@)\1\\: \1w"   345 EnterYourBirthday
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your location\1\\ (e.g. city, state)\1\\: \1w" 346 EnterYourCityState
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yEnter your zip (or postal) code\1\\: \1w"   347 EnterYourZipCode
+@@ -418 +426 @@
+-"\1bSysop is        : \1c"                                355 LiSysopIs
++"\1b\1hSysop is        : \1c%s\r\n"                     355 LiSysopIs
+@@ -434 +442 @@
+-"Do you have a color terminal"                          367 ColorTerminalQ
++"Does your terminal display colors"                     367 ColorTerminalQ
+@@ -439 +447 @@
+-"Does your terminal support IBM extended ASCII"         369 ExAsciiTerminalQ
++"Does your terminal support IBM extended ASCII (CP437)"     369 ExAsciiTerminalQ
+@@ -455,4 +463,6 @@
+-"\1l\1n\1c\1h%s \1n\1cExternal Programs:\r\n\r\n"             380 XtrnProgLstHdr
+-"\1n\1cNum \1h�\1n\1c Name                           "       381 XtrnProgLstTitles
+-"\1h�������������������������������      "               382 XtrnProgLstUnderline
+-"\1h\1c%3u � \1n\1c%-25.25s\1h      "                        383 XtrnProgLstFmt
++"\1n\1c\1h%s \1n\1cExternal Programs:\r\n\r\n"             380 XtrnProgLstHdr
++"\1n\1cNum \1h\xb3\1n\1c Name                             "       381 XtrnProgLstTitles
++"\1h\xc4\xc4\xc4\xc4�\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\ 382 XtrnProgLstUnderline
++	"\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"\
++	"\xc4\xc4\xc4\xc4\xc4\xc4 "
++"\1h\1c%3u \xb3 \1n\1c%-32.32s\1h "                        383 XtrnProgLstFmt
+@@ -473 +483 @@
+-"\r\n\1m\1hChecking Slots..."                             395 CheckingSlots
++"\r\n\1m\1hChecking Slots @ELLIPSIS@"                          395 CheckingSlots
+@@ -537 +547 @@
+-"\1_\1y\1hUser edit (\1w?\1y=Menu) "\                    441 UeditPrompt
++"\1_\1y\1hUser edit (\1w\1`?\1y=Menu) "\                    441 UeditPrompt
+@@ -576 +586 @@
+-"\1n\1b[\1h\1wL\1n\1b] \1hScreen Length                \1n\1b\1\\: \1c%s\r\n"   477 UserDefaultsRows
++"\1n\1b[\1h\1wL\1n\1b] \1hScreen Length                \1n\1b\1\\: \1c%s %s\r\n" 477 UserDefaultsRows
+@@ -597 +607 @@
+-"\r\n\1_\1b\1h[\1c�\1b] \1yHow many rows on your monitor "\   497 HowManyRows
++"\r\n\1_\1b\1h[\1c@CHECKMARK@\1b] \1yHow many rows on your monitor "\   497 HowManyRows
+@@ -601 +611 @@
+-"\1_\1b\1h[\1c�\1b] \1yNetwork mail address\1\\ "\      500 EnterNetMailAddress
++"\1_\1b\1h[\1c@CHECKMARK@\1b] \1yNetwork mail address\1\\ "\      500 EnterNetMailAddress
+@@ -706 +716 @@
+-"\r\n\1_\1h\1wUnpacking..."                                576 QWKUnpacking
++"\r\n\1_\1h\1wUnpacking @ELLIPSIS@"                          576 QWKUnpacking
+@@ -735,3 +745,3 @@
+-"\r\n\1_\1w\1hUsing your real name...\r\n"                 599 UsingRealName
+-"\r\n\1_\1w\1hPosting Privately...\r\n"                    600 PostingPrivately
+-"\r\n\1_\1w\1hPosting anonymously...\r\n"                  601 PostingAnonymously
++"\r\n\1_\1w\1hUsing your real name @ELLIPSIS@\r\n"         599 UsingRealName
++"\r\n\1_\1w\1hPosting Privately @ELLIPSIS@\r\n"            600 PostingPrivately
++"\r\n\1_\1w\1hPosting anonymously @ELLIPSIS@\r\n"          601 PostingAnonymously
+@@ -756 +766 @@
+-"\1n\1m\r\nSending NetMail To: \1h%s\1n\1m (\1h%s\1n\1m)\r\n"\  619 NetMailing
++"\1n\1?\1mSending NetMail To: \1h%s\1n\1m (\1h%s\1n\1m)\r\n"\  619 NetMailing
+@@ -777 +787 @@
+-"\1h��� �������������������������    ����"               638 ChatChanLstUnderline
++"\1h\xc4\xc4\xc4 \xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4    \xc4\xc4\xc4\xc4"               638 ChatChanLstUnderline
+@@ -800,5 +810,5 @@
+-	"(\1h%u\1n\1c) %s (\1h?\1n\1c=Menu) (\1h%u\1n\1c Files\1n\1c): \1n"
+-"\1n\1>\1q\1l\x014 \1h\1y[\1wD\1y]ownload  \1h\1y[\1wB\1y]atch  "\                   661 FileListBatchCommands
+-	"[\1wV\1y]iew Content  [\1wE\1y]xtended Info  "\
+-	"[\1wP\1y]revious Page  [\1wQ\1y]uit\1>\1n\r\n"
+-"\r\nDownload attached file: \1w%s\1b (%s bytes)"         662 DownloadAttachedFileQ
++	"(\1h%u\1n\1c) %s (\1h\1`?\1n\1c=Menu) (\1h%u\1n\1c of \1h%u\1n\1c Files\1n\1c): \1n\1~"
++"\1n\1>\1q\1l\x014 \1h\1y[\1w\1~D\1y]ownload  \1h\1y[\1w\1~B\1y]atch  "\ 661 FileListBatchCommands
++	"[\1w\1~V\1y]iew Content  [\1w\1~E\1y]xtended Info  "\
++	"[\1w\1~P\1y]revious Page  [\1w\1~Q\1y]uit\1>\1n\r\n"
++"\1\?Download attached file: \1w%s\1b (%s bytes)"         662 DownloadAttachedFileQ
+@@ -840 +850 @@
+-	"���� ����������������������������������\r\n"
++	"\xc4\xc4\xc4\xc4 \xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\r\n"
+@@ -905 +915 @@
+-"\1n\r\nSorry, you can't delete this message.\r\n"	758 CantDeleteMsg
++"\1n\r\nSorry, you can't delete message #%lu.\r\n"	758 CantDeleteMsg
+@@ -911 +921 @@
+-"\1n\1hStarting new user registration...\r\n"           764 StartingNewUserRegistration
++"\1n\1hStarting new user registration @ELLIPSIS@\r\n"   764 StartingNewUserRegistration
+@@ -952 +962 @@
+-"\r\n� \1bMsg \1n\1b: \1h\1cVoted Up %u times%s and Down %u times%s (Score: %d)"          784 MsgVotes
++"\r\n\xb3 \1bMsg \1n\1b: \1h\1cVoted Up %u times%s and Down %u times%s (Score: %d)"          784 MsgVotes
+@@ -965 +975 @@
+-"\1n\1mMIME-decoded plain-text:\1n\r\n"     797 MIMEDecodedPlainText
++"\1n\1mMIME-decoded %s %s-text:\1n\r\n"  797 MIMEDecodedPlainTextFmt
+@@ -973 +983 @@
+-"\r\n� \1bTags\1n\1b: \1h\1c%.70s"          805 MsgTags
++"\r\n\xb3 \1bTags\1n\1b: \1h\1c%.70s"          805 MsgTags
+@@ -975 +985 @@
+-"\r\n "                                     807 LongLineContinuationPrefix
++"\r\n"                                      807 LongLineContinuationPrefix
+@@ -978 +988,37 @@
+-"\1[\1>"                                    810 Scanned
+\ No newline at end of file
++"\1[\1>"                                    810 Scanned
++"\1n\1b\1h[\1c@CHECKMARK@\1b] \1h\1yHIT your \1wBACKSPACE\1y or DELETE-LEFT key: " 811 HitYourBackspaceKey
++"\1\\\1n\1cCharacter \1h%u (%02Xh) \1n\1creceived.\r\n"   812 CharacterReceivedFmt
++"\1r\1h!Unsupported backspace key: \1w%02Xh\r\n"          813 InvalidBackspaceKeyFmt
++""                                                        814 SwapDeleteKeyQ
++"\1n\1hCBM/\1yPETSCII\1w terminal detected.\r\n"          815 PetTerminalDetected
++"Are you using a CBM/PETSCII terminal"                    816 PetTerminalQ
++"Auto "                                                   817 TerminalAutoDetect
++"columns"                                                 818 TerminalColumns
++"rows"                                                    819 TerminalRows
++"(mono) "                                                 820 TerminalMonochrome
++"(color) "                                                821 TerminalColor
++"(iCE color) "                                            822 TerminalIceColor
++"Does your terminal display bright background "\          823 IceColorTerminalQ
++    "(iCE) colors\r\n    \1n"\
++    "(i.e. \1i\1k\x013this text\1n is black on yellow and NOT BLINKING)"
++"Does your terminal support UTF-8"                        824 Utf8TerminalQ
++"\r\n\xb3 \1bCC  \1n\1b: \1h\1c%.70s"                     825 MsgCarbonCopyList
++"\1n\1hLogging on to @BBS@ as @ALIAS@ @ELLIPSIS@\1n\r\n"\ 826 LoggingOn
++    "@RESETPAUSE@"
++"\1n\1b[\1h\1wU\1n\1b] \1hInclude UTF-8 Characters     \1n\1b: \1c%s\r\n"	827 QWKSettingsUtf8
++"\1n\1m\1h%s\1n\1m%.0s posted to you "\                                     828 MsgPostedToYouVia
++    "on \1h%s \1n\1m%s\r\n" 
++"unlimited"                                               829 Unlimited
++" via raw"						                          830 NodeConnectionRaw
++"Does your terminal support mouse reporting"              831 MouseTerminalQ
++"(mouse) "                                                832 TerminalMouse
++"->)\xb3(<-<(\xb3)>"                                      833 SpinningCursor0
++"\xb3/\xc4\\"                                             834 SpinningCursor1
++"\xb3\\\xc4/"                                             835 SpinningCursor2
++"\xb0\xb1\xb2\xdb\xb2\xb1"                                836 SpinningCursor3
++"-=\xf0="                                                 837 SpinningCursor4
++"\xda\xc0\xd9\xbf"                                        838 SpinningCursor5
++"\xda\xbf\xd9\xc0"                                        839 SpinningCursor6
++"\xdc\xde\xdf\xdd"                                        840 SpinningCursor7
++"\xdc\xdd\xdf\xde"                                        841 SpinningCursor8
++"\xfa\xf9\xfe\xf9"                                        842 SpinningCursor9
+\ No newline at end of file
-- 
GitLab


From e99c4a091372a8f7817ec240ba30ef74fb803b60 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 01:37:38 -0700
Subject: [PATCH 738/752] Ignore build artifacts in this dir.

---
 src/sbbs3/chat/.gitignore | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 src/sbbs3/chat/.gitignore

diff --git a/src/sbbs3/chat/.gitignore b/src/sbbs3/chat/.gitignore
new file mode 100644
index 0000000000..e51f4ec7fb
--- /dev/null
+++ b/src/sbbs3/chat/.gitignore
@@ -0,0 +1,3 @@
+chat.drc
+chat.mak
+chat.tds
\ No newline at end of file
-- 
GitLab


From 75f1d3f75332e6e7924223ecd9afb86207f788a6 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 01:41:41 -0700
Subject: [PATCH 739/752] Put the release build of fmsgdump in the right place.

---
 src/sbbs3/fmsgdump.vcxproj | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/sbbs3/fmsgdump.vcxproj b/src/sbbs3/fmsgdump.vcxproj
index a35c81eed8..a3c90df3f3 100644
--- a/src/sbbs3/fmsgdump.vcxproj
+++ b/src/sbbs3/fmsgdump.vcxproj
@@ -53,6 +53,8 @@
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <LinkIncremental>false</LinkIncremental>
+    <OutDir>.\msvc.win32.exe.release\</OutDir>
+    <IntDir>.\msvc.win32.release\fmsgdump</IntDir>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
-- 
GitLab


From a0cffa8c08d25a4c3422e6cbac037d2ab5aeaab7 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 02:16:02 -0700
Subject: [PATCH 740/752] Don't try to load/activate fonts if not online.

---
 exec/load/fonts.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec/load/fonts.js b/exec/load/fonts.js
index 72d6e7a04d..bc15c6a721 100644
--- a/exec/load/fonts.js
+++ b/exec/load/fonts.js
@@ -57,7 +57,7 @@ function fonts(key)
 	}
 }
 
-if(cterm.supports_fonts() != false)
+if(bbs.online && cterm.supports_fonts() != false)
 	for(var i in argv)
 		fonts(argv[i]);
 
-- 
GitLab


From a09afd3c3bf1352a019ef673be653b5e911dd415 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 04:20:49 -0700
Subject: [PATCH 741/752] Fixed: Monsters did not move on 64-bit systems

The game is much easier to play when the monsters don't move. :-)

A clock_t is bigger than an int on 64-bit Linux systems, so there
was some weirdness with the tick offset that caused monsters to
never move.
---
 xtrn/tbd/tbd.c | 12 ++++++------
 xtrn/tbd/tbd.h |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/xtrn/tbd/tbd.c b/xtrn/tbd/tbd.c
index cab7b7801c..d73e787887 100644
--- a/xtrn/tbd/tbd.c
+++ b/xtrn/tbd/tbd.c
@@ -32,8 +32,8 @@ unsigned _stklen=20000;
 
 char     redraw_screen;
 long     record_number;
-int      create_log,chfile,rmfile,weapon_ready,invisible,strong,
-                tpic,lasthit,clock_tick,clock_tick2,ateof;
+int      chfile,rmfile,weapon_ready,invisible,strong,tpic,lasthit,ateof;
+clock_t  clock_tick,clock_tick2;
 uchar    map[LEVELS][SQUARE][SQUARE];
 
 void exitfunc(void)
@@ -53,7 +53,7 @@ void exitfunc(void)
 int main(int argc, char **argv)
 {
     FILE *fp;
-    char str[256],chbuf[8],*buf,*p,name[26];
+    char str[256],*buf,*p,name[26];
     int file,x,r1,r2,ch,times_played=0,lev,maint_only=0;
     long lastrun,length,l,exp;
     uchar uch;
@@ -308,7 +308,7 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
     long timeleftmin;
     time_t strength_timer,invis_timer,now,timeout,health_timer;
     node_t node;
-    int tick_offset=0;
+    clock_t tick_offset=0;
 
     clock_tick=invisible=strong=0; clock_tick2=40;
     printfile("tbd.mnu"); game_commands(0,-1);
@@ -336,9 +336,9 @@ void movement(int sx,int sy,int sz,int sgx,int sgy)
                                object[rmobj[(gy*11)+gx].item].name); } }
 
     timeout=time(NULL);                         /* Set timer on entry point */
-    tick_offset=(int)(msclock()/(MSCLOCKS_PER_SEC/18.2))%19;
+    tick_offset=(clock_t)(msclock()/(MSCLOCKS_PER_SEC/18.2))%19;
     do {
-        clock_tick=(int)(msclock()/(MSCLOCKS_PER_SEC/18.2))%19-tick_offset;
+        clock_tick=(clock_t)(msclock()/(MSCLOCKS_PER_SEC/18.2))%19-tick_offset;
         if(clock_tick<0)
             clock_tick+=19;
         ++clock_tick2;
diff --git a/xtrn/tbd/tbd.h b/xtrn/tbd/tbd.h
index 7bf7a5ad88..fa9d96c510 100644
--- a/xtrn/tbd/tbd.h
+++ b/xtrn/tbd/tbd.h
@@ -122,7 +122,7 @@ extern long cost_per_min,times_per_day,total_cost;
 
 extern char     redraw_screen;
 extern long     record_number;
-extern int      create_log,chfile,rmfile,weapon_ready,invisible,strong,
-                tpic,lasthit,clock_tick,clock_tick2,ateof;
+extern int      chfile,rmfile,weapon_ready,invisible,strong,tpic,lasthit,ateof;
+extern clock_t	clock_tick;
 extern uchar    map[LEVELS][SQUARE][SQUARE];
 
-- 
GitLab


From 09a305a5cdf8a1b9383e783806c304a13c731778 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 12:05:15 -0700
Subject: [PATCH 742/752] Allow additional installation target
 categories/sections per program

---
 exec/install-xtrn.js | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index d9e69df0b9..d61e4088bd 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -27,6 +27,7 @@
 //
 // [prog:<code>]
 // 		name 			= program name or description (40 chars max)
+//      cats            = additional target installation categories (sections)
 //		cmd 			= command-line to execute (63 chars max)
 //		clean_cmd 		= clean-up command-line, if needed (63 chars max)
 //		settings 		= bit-flags (see XTRN_* in sbbsdefs.js)
@@ -90,7 +91,7 @@
 
 "use strict";
 
-const REVISION = "$Revision: 1.14 $".split(' ')[1];
+const REVISION = "3.18b";
 const ini_fname = "install-xtrn.ini";
 
 load("sbbsdefs.js");
@@ -115,7 +116,12 @@ function install_xtrn_item(cnf, type, name, desc, item, cats)
 
 	if (!item.name)
 		item.name = name || item.code;
-	
+
+	if(item.cats)
+		item.cats = item.cats.split(',').concat(cats);
+	else
+		item.cats = cats;
+
 	function find_code(objs, code)
 	{
 		if (!options.overwrite) {
@@ -160,7 +166,7 @@ function install_xtrn_item(cnf, type, name, desc, item, cats)
 			return "No external program sections have been created";
 
 		for (var i = 0; i < xtrn_area.sec_list.length; i++) {
-			if(cats.indexOf(xtrn_area.sec_list[i].name) >= 0
+			if(item.cats.indexOf(xtrn_area.sec_list[i].name) >= 0
 				&& confirm("Install " + item.name + " into " + xtrn_area.sec_list[i].name + " section")) {
 				item.sec = xtrn_area.sec_list[i].number;
 				break;
-- 
GitLab


From 37349ddb5a80115fd25467d374550444af779c3a Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 12:17:10 -0700
Subject: [PATCH 743/752] Add the config / reset script to the configuration
 too

---
 xtrn/tw2/install-xtrn.ini | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/xtrn/tw2/install-xtrn.ini b/xtrn/tw2/install-xtrn.ini
index c9613009b7..dbec441035 100644
--- a/xtrn/tw2/install-xtrn.ini
+++ b/xtrn/tw2/install-xtrn.ini
@@ -3,13 +3,19 @@ Desc: A multiplayer game that is a cross between a war game and a space trading
 By: Chris Sherrick, ported to Sync-JS by Deuce (Stephen Hurd)
 Cats: Games
 Subs: Space, War, Trade, Classic, Multiplayer, JavaScript, Port
-Inst: $Id: install-xtrn.ini,v 1.2 2020/04/21 03:25:59 rswindell Exp $
+Inst: 2020/09/20
 
 [prog:TW2]
 cmd  = ?tw2.js
 settings = XTRN_MULTIUSER
 required = true
 
+[prog:TW2-CFG]
+name = Trade Wars v.ii - Configure / Reset
+cats = Operator
+cmd  = ?twint500.js
+ars  = SYSOP
+
 !include install-json-service.ini
 
 [ini:json-service.ini:tw2]
-- 
GitLab


From 755735b1c9acc2fc778918351f965079c4a429ed Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 12:17:32 -0700
Subject: [PATCH 744/752] Be clear what it is we are configuring

Side note: Is it "Trade Wars", "TradeWars", or "Tradewars"? All 3 are used
interachangeably. I'm going with the former.
---
 xtrn/tw2/twint500.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xtrn/tw2/twint500.js b/xtrn/tw2/twint500.js
index e337a0c01c..334976870b 100644
--- a/xtrn/tw2/twint500.js
+++ b/xtrn/tw2/twint500.js
@@ -36,7 +36,7 @@ function ConfigureSettings()
 		for(i=0; i<GameSettingProperties.length; i++)
 			list.push(format("%-35s %s",GameSettingProperties[i].name,Settings[GameSettingProperties[i].prop]));
 
-		i=uifc.list(WIN_MID|WIN_ORG|WIN_ACT|WIN_ESC, 0, 0, 0, last, last, "Configuration", list);
+		i=uifc.list(WIN_MID|WIN_ORG|WIN_ACT|WIN_ESC, 0, 0, 0, last, last, "Trade Wars 2 Configuration", list);
 		if(i==-1) {
 			if(uifc.changes) {
 				var q=uifc.list(WIN_MID|WIN_SAV, 0, 0, 0, 0, 0, "Save Changes?", ["Yes", "No"]);
@@ -104,7 +104,7 @@ var reset = false;
 if(this.uifc == undefined) {
 
 	while(!js.terminated && (!this.console || !console.aborted)) {
-		print("Configuration:");
+		print("Trade Wars 2 Configuration:");
 		print();
 		for(var i=0; i< GameSettingProperties.length; i++)
 			print(format("%2d: %-35s = %s", i + 1, GameSettingProperties[i].name, Settings[GameSettingProperties[i].prop]));
@@ -149,7 +149,7 @@ if(this.uifc == undefined) {
 		alert("WARNING: Unable to connect to server: " + e);
 	}
 } else {
-	uifc.init("TradeWars 2 Initialization", /* ciolibmode: */argv[0]);
+	uifc.init("Trade Wars 2 Initialization", /* ciolibmode: */argv[0]);
 	ConfigureSettings();
 
 	if(js.global.db != undefined) {
-- 
GitLab


From 2bff2aefa3f74b0f41e3aa6960660e2afdba6823 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 12:19:52 -0700
Subject: [PATCH 745/752] Install into the "Operator" section by default, if
 there is one.

---
 xtrn/lord/install-xtrn.ini | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xtrn/lord/install-xtrn.ini b/xtrn/lord/install-xtrn.ini
index 9e7a4694a0..4ff116adfc 100644
--- a/xtrn/lord/install-xtrn.ini
+++ b/xtrn/lord/install-xtrn.ini
@@ -5,7 +5,7 @@ Desc: The ever-popular multi-player battle game created for Bulletin Board Syste
 By:   Seth Able Robinson, Deuce (Stephen Hurd)
 Cats: Games
 Subs: Adventure, Classic, Multiplayer, JavaScript, Port
-Inst: 2020/09/07
+Inst: 2020/09/20
 
 [prog:LORD]
 cmd  = ?lord
@@ -15,13 +15,13 @@ required = true
 
 [prog:LORD-CFG]
 name = Legend of the Red Dragon - Configure
-note = If you have an "Operator" section, you may want to install this there
+cats = Operator
 cmd  = ?editfile %slord.ini
 ars  = SYSOP
 
 [prog:LORD-RST]
 name = Legend of the Red Dragon - Reset
-note = If you have an "Operator" section, you may want to install this there
+cats = Operator
 cmd  = ?lord RESET
 ars  = SYSOP
 
-- 
GitLab


From ecbee37a198623bbc5ffcd158aba863420e9dc56 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 12:20:33 -0700
Subject: [PATCH 746/752] Use current settings as defaults for US-timezone and
 DST prompts.

---
 src/sbbs3/scfg/scfgsys.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index 53c526cf2d..d6b0e86fc1 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -39,7 +39,9 @@ static void configure_dst(void)
 	strcpy(opt[1],"No");
 	strcpy(opt[2],"Automatic");
 	opt[3][0]=0;
-	int i=1;
+	int i = 2;
+	if(!(cfg.sys_misc & SM_AUTO_DST))
+		i = !(cfg.sys_timezone & DAYLIGHT);
 	uifc.helpbuf=
 		"`Daylight Saving Time (DST):`\n"
 		"\n"
@@ -172,7 +174,7 @@ void sys_cfg(void)
 				uifc.input(WIN_MID,0,0,"Location",cfg.sys_location,sizeof(cfg.sys_location)-1,K_EDIT);
 				break;
 			case 2:
-				i=0;
+				i = !(cfg.sys_timezone & US_ZONE);
 				uifc.helpbuf=
 					"`United States Time Zone:`\n"
 					"\n"
-- 
GitLab


From 023fb5c059135f9970171155c44a576d89644fdb Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 18:47:15 -0700
Subject: [PATCH 747/752] Remove the option to run twint500.js during install

Doesn't seem to work. Maybe the service has to be install and up first
(cause it worked at one time). Oh well, with the menu option added, the
sysop can just reset it the first time manually.
---
 xtrn/tw2/install-xtrn.ini | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/xtrn/tw2/install-xtrn.ini b/xtrn/tw2/install-xtrn.ini
index dbec441035..dae313f764 100644
--- a/xtrn/tw2/install-xtrn.ini
+++ b/xtrn/tw2/install-xtrn.ini
@@ -21,6 +21,3 @@ ars  = SYSOP
 [ini:json-service.ini:tw2]
 keys=dir
 values=startup_dir
-
-[exec:twint500.js]
-prompt=Configure and Initialize TW2
\ No newline at end of file
-- 
GitLab


From 34507e5e14c3c257c400c1f55594146626f8d3eb Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 18:53:02 -0700
Subject: [PATCH 748/752] Exit cleanly even when not resetting the game.

---
 xtrn/tw2/twint500.js | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/xtrn/tw2/twint500.js b/xtrn/tw2/twint500.js
index 334976870b..1c064c7d1e 100644
--- a/xtrn/tw2/twint500.js
+++ b/xtrn/tw2/twint500.js
@@ -158,28 +158,27 @@ if(this.uifc == undefined) {
 	}
 }
 
-if(!reset)
-	exit(0);
+if(reset) {
+	print("Resetting game");
+	load(fname("ports.js"));
+	load(fname("planets.js"));
+	load(fname("teams.js"));
+	load(fname("sectors.js"));
+	load(fname("maint.js"));
+	load(fname("players.js"));
+	load(fname("messages.js"));
+	load(fname("computer.js"));
+	load(fname("input.js"));
 
-print("Resetting game");
-load(fname("ports.js"));
-load(fname("planets.js"));
-load(fname("teams.js"));
-load(fname("sectors.js"));
-load(fname("maint.js"));
-load(fname("players.js"));
-load(fname("messages.js"));
-load(fname("computer.js"));
-load(fname("input.js"));
-
-ResetAllPlayers();
-ResetAllPlanets();
-ResetAllMessages();
-InitializeTeams();
-InitializeSectors();
-InitializePorts();
-InitializeCabal();
-db.write(Settings.DB,'twopeng',[],LOCK_WRITE);
+	ResetAllPlayers();
+	ResetAllPlanets();
+	ResetAllMessages();
+	InitializeTeams();
+	InitializeSectors();
+	InitializePorts();
+	InitializeCabal();
+	db.write(Settings.DB,'twopeng',[],LOCK_WRITE);
+}
 if(this.uifc) {
 	uifc.pop();
 	uifc.bail();
-- 
GitLab


From efb31c538f7a731bea2e5ae97a352d781dfab10b Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 19:18:46 -0700
Subject: [PATCH 749/752] Prioritize the order of install-target categories
 from the install-xtrn.ini

---
 exec/install-xtrn.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js
index d61e4088bd..729b0b6a17 100644
--- a/exec/install-xtrn.js
+++ b/exec/install-xtrn.js
@@ -165,10 +165,11 @@ function install_xtrn_item(cnf, type, name, desc, item, cats)
 		if (!xtrn_area.sec_list.length)
 			return "No external program sections have been created";
 
-		for (var i = 0; i < xtrn_area.sec_list.length; i++) {
-			if(item.cats.indexOf(xtrn_area.sec_list[i].name) >= 0
-				&& confirm("Install " + item.name + " into " + xtrn_area.sec_list[i].name + " section")) {
-				item.sec = xtrn_area.sec_list[i].number;
+		for (var i = 0; i < item.cats.length; i++) {
+			var code = item.cats[i].toLowerCase();
+			if(xtrn_area.sec[code]
+				&& confirm("Install " + item.name + " into " + xtrn_area.sec[code].name + " section")) {
+				item.sec = xtrn_area.sec[code].number;
 				break;
 			}
 		}
-- 
GitLab


From bbb361fe973526e69fb6b4e46ec6ca808b0eccba Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 19:27:24 -0700
Subject: [PATCH 750/752] Force a screen pause on abnormal exit.

---
 xtrn/tw2/tw2.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/xtrn/tw2/tw2.js b/xtrn/tw2/tw2.js
index 4f7c393629..bd0aa82409 100644
--- a/xtrn/tw2/tw2.js
+++ b/xtrn/tw2/tw2.js
@@ -322,6 +322,7 @@ try {
 		console.attributes="R";
 		console.writeln("The game has not been initialized.");
 		console.writeln("Please notify the SysOp.");
+		console.pause();
 		exit(0);
 	}
 	
@@ -332,8 +333,10 @@ try {
 	console.attributes="W";
 	console.writeln("Initializing...");
 	console.writeln("Searching my records for your name.");
-	if(!LoadPlayer())
+	if(!LoadPlayer()) {
+		console.pause();
 		exit(0);
+	}
 
 	console.pause();
 	while(player.KilledBy==0 && exit_tw2==false) {
-- 
GitLab


From 3a7334b0a6292206089295b810bd1e01c4385579 Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 20 Sep 2020 19:38:20 -0700
Subject: [PATCH 751/752] Add John Morris' name, change name from "Trade Wars
 v.ii" to "Trade Wars 2"

---
 xtrn/tw2/install-xtrn.ini | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xtrn/tw2/install-xtrn.ini b/xtrn/tw2/install-xtrn.ini
index dae313f764..2c62c2e7ed 100644
--- a/xtrn/tw2/install-xtrn.ini
+++ b/xtrn/tw2/install-xtrn.ini
@@ -1,6 +1,6 @@
-Name: Trade Wars v.ii
+Name: Trade Wars 2 - 500 Sectors
 Desc: A multiplayer game that is a cross between a war game and a space trading game.
-By: Chris Sherrick, ported to Sync-JS by Deuce (Stephen Hurd)
+By: Chris Sherrick, John Morris, ported to Sync-JS by Deuce (Stephen Hurd)
 Cats: Games
 Subs: Space, War, Trade, Classic, Multiplayer, JavaScript, Port
 Inst: 2020/09/20
@@ -11,7 +11,7 @@ settings = XTRN_MULTIUSER
 required = true
 
 [prog:TW2-CFG]
-name = Trade Wars v.ii - Configure / Reset
+name = Trade Wars 2 - Configure / Reset
 cats = Operator
 cmd  = ?twint500.js
 ars  = SYSOP
-- 
GitLab


From e42a394d2ffb1e961edb187217deeb3ad723d4e2 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sun, 20 Sep 2020 20:52:14 -0700
Subject: [PATCH 752/752] Remove the old Java Telnet app that we haven't used -
 in a decade?

---
 web/root/telnet/BUGS                          |   19 -
 web/root/telnet/CHANGES                       |  563 -----
 web/root/telnet/COPYING                       |  333 ---
 web/root/telnet/COPYING.LIB                   |  462 ----
 web/root/telnet/CharDisplay.html              |   16 -
 web/root/telnet/CharDisplayTest.class         |  Bin 4437 -> 0 bytes
 web/root/telnet/CharDisplayTest.java          |  163 --
 .../Documentation/Source/CharDisplayTest.html |  495 ----
 .../Source/allclasses-frame.html              |   61 -
 .../Documentation/Source/appWrapper.html      |  637 ------
 .../Documentation/Source/deprecated-list.html |   91 -
 .../Source/display/CharDisplay.html           | 1563 -------------
 .../Source/display/SoftFont.html              |  242 --
 .../Source/display/Terminal.html              |  528 -----
 .../Source/display/TerminalHost.html          |  178 --
 .../Source/display/package-frame.html         |   43 -
 .../Source/display/package-summary.html       |  131 --
 .../Source/display/package-tree.html          |  117 -
 .../Documentation/Source/display/vt320.html   |  842 -------
 .../telnet/Documentation/Source/help-doc.html |  146 --
 .../Source/images/blue-ball-small.gif         |  Bin 255 -> 0 bytes
 .../Documentation/Source/images/blue-ball.gif |  Bin 925 -> 0 bytes
 .../Source/images/class-index.gif             |  Bin 1497 -> 0 bytes
 .../Documentation/Source/images/constrct.gif  |  Bin 1565 -> 0 bytes
 .../Source/images/constructor-index.gif       |  Bin 1711 -> 0 bytes
 .../Source/images/constructors.gif            |  Bin 1565 -> 0 bytes
 .../Source/images/cyan-ball-small.gif         |  Bin 255 -> 0 bytes
 .../Documentation/Source/images/cyan-ball.gif |  Bin 925 -> 0 bytes
 .../Source/images/error-index.gif             |  Bin 1438 -> 0 bytes
 .../Source/images/exception-index.gif         |  Bin 1707 -> 0 bytes
 .../Documentation/Source/images/field_ix.gif  |  Bin 1443 -> 0 bytes
 .../Documentation/Source/images/fields.gif    |  Bin 1241 -> 0 bytes
 .../Source/images/green-ball-small.gif        |  Bin 102 -> 0 bytes
 .../Source/images/green-ball.gif              |  Bin 886 -> 0 bytes
 .../Source/images/interface-index.gif         |  Bin 1648 -> 0 bytes
 .../Source/images/magenta-ball-small.gif      |  Bin 104 -> 0 bytes
 .../Source/images/magenta-ball.gif            |  Bin 896 -> 0 bytes
 .../Source/images/method-index.gif            |  Bin 1588 -> 0 bytes
 .../Documentation/Source/images/methods.gif   |  Bin 1403 -> 0 bytes
 .../Source/images/package-index.gif           |  Bin 1607 -> 0 bytes
 .../Source/images/red-ball-small.gif          |  Bin 255 -> 0 bytes
 .../Documentation/Source/images/red-ball.gif  |  Bin 527 -> 0 bytes
 .../Source/images/variable-index.gif          |  Bin 1576 -> 0 bytes
 .../Documentation/Source/images/variables.gif |  Bin 1241 -> 0 bytes
 .../Source/images/yellow-ball-small.gif       |  Bin 255 -> 0 bytes
 .../Source/images/yellow-ball.gif             |  Bin 925 -> 0 bytes
 .../Documentation/Source/index-all.html       |  801 -------
 .../telnet/Documentation/Source/index.html    |   25 -
 .../Source/modules/BSXModule.html             |  596 -----
 .../Source/modules/ButtonBar.html             |  636 ------
 .../Documentation/Source/modules/Module.html  |  235 --
 .../Source/modules/MudConnector.html          |  571 -----
 .../Documentation/Source/modules/Script.html  |  348 ---
 .../Source/modules/TextLabel.html             |  523 -----
 .../Source/modules/package-frame.html         |   45 -
 .../Source/modules/package-summary.html       |  136 --
 .../Source/modules/package-tree.html          |  124 -
 .../Documentation/Source/overview-frame.html  |   40 -
 .../Source/overview-summary.html              |  108 -
 .../Documentation/Source/overview-tree.html   |  139 --
 .../telnet/Documentation/Source/package-list  |    3 -
 .../telnet/Documentation/Source/packages.html |   26 -
 .../telnet/Documentation/Source/proxy.html    |  252 --
 .../Documentation/Source/serialized-form.html | 1635 -------------
 .../Source/socket/StatusPeer.html             |  181 --
 .../Documentation/Source/socket/TelnetIO.html |  408 ----
 .../Source/socket/TelnetWrapper.html          |  679 ------
 .../Source/socket/TimedOutException.html      |  228 --
 .../Source/socket/package-frame.html          |   50 -
 .../Source/socket/package-summary.html        |  138 --
 .../Source/socket/package-tree.html           |  115 -
 .../Documentation/Source/stylesheet.css       |   29 -
 .../telnet/Documentation/Source/telnet.html   |  955 --------
 web/root/telnet/Documentation/images/bin.gif  |  Bin 1272 -> 0 bytes
 .../telnet/Documentation/images/border.gif    |  Bin 459 -> 0 bytes
 .../telnet/Documentation/images/check.gif     |  Bin 230 -> 0 bytes
 .../telnet/Documentation/images/checkS.gif    |  Bin 139 -> 0 bytes
 web/root/telnet/Documentation/images/doc.gif  |  Bin 922 -> 0 bytes
 .../telnet/Documentation/images/flash.gif     |  Bin 167 -> 0 bytes
 web/root/telnet/Documentation/images/left.gif |  Bin 497 -> 0 bytes
 web/root/telnet/Documentation/images/mail.gif |  Bin 1069 -> 0 bytes
 web/root/telnet/Documentation/images/star.gif |  Bin 227 -> 0 bytes
 .../telnet/Documentation/images/testit.gif    |  Bin 727 -> 0 bytes
 web/root/telnet/Documentation/images/user.gif |  Bin 788 -> 0 bytes
 web/root/telnet/Documentation/index.html      |  696 ------
 web/root/telnet/Documentation/user.html       |  288 ---
 .../telnet/Documentation/whatis.telnet.html   |   31 -
 web/root/telnet/INSTALL                       |   55 -
 web/root/telnet/IOtest.class                  |  Bin 1589 -> 0 bytes
 web/root/telnet/IOtest.java                   |   61 -
 web/root/telnet/Makefile                      |  178 --
 web/root/telnet/README                        |   55 -
 web/root/telnet/REVISION                      |   20 -
 web/root/telnet/TODO                          |   22 -
 web/root/telnet/appWrapper.class              |  Bin 5619 -> 0 bytes
 web/root/telnet/appWrapper.java               |  256 ---
 web/root/telnet/classes.zip                   |  Bin 85685 -> 0 bytes
 web/root/telnet/display/CharDisplay.class     |  Bin 19835 -> 0 bytes
 web/root/telnet/display/CharDisplay.java      | 1297 -----------
 web/root/telnet/display/SoftFont.class        |  Bin 10793 -> 0 bytes
 web/root/telnet/display/SoftFont.java         | 1005 --------
 web/root/telnet/display/Terminal.class        |  Bin 509 -> 0 bytes
 web/root/telnet/display/Terminal.java         |   70 -
 web/root/telnet/display/TerminalHost.class    |  Bin 255 -> 0 bytes
 web/root/telnet/display/TerminalHost.java     |   43 -
 web/root/telnet/display/vt320.class           |  Bin 30926 -> 0 bytes
 web/root/telnet/display/vt320.java            | 2021 -----------------
 web/root/telnet/examples/bbs.html             |   33 -
 web/root/telnet/examples/bbs2.html            |   29 -
 web/root/telnet/examples/bbs3.html            |   26 -
 web/root/telnet/examples/lh.html              |   34 -
 web/root/telnet/examples/mg.html              |   49 -
 web/root/telnet/examples/tapp.html            |   21 -
 web/root/telnet/frame.class                   |  Bin 817 -> 0 bytes
 web/root/telnet/frame.java                    |   49 -
 web/root/telnet/ibmtest.html                  |   31 -
 web/root/telnet/index.download.html           |  182 --
 web/root/telnet/index.ssjs                    |    2 -
 web/root/telnet/modules/BSXModule.class       |  Bin 10405 -> 0 bytes
 web/root/telnet/modules/BSXModule.java        |  562 -----
 web/root/telnet/modules/ButtonBar.class       |  Bin 6731 -> 0 bytes
 web/root/telnet/modules/ButtonBar.java        |  388 ----
 web/root/telnet/modules/Module.class          |  Bin 419 -> 0 bytes
 web/root/telnet/modules/Module.java           |   61 -
 web/root/telnet/modules/MudConnector.class    |  Bin 7696 -> 0 bytes
 web/root/telnet/modules/MudConnector.java     |  300 ---
 web/root/telnet/modules/Script.class          |  Bin 2809 -> 0 bytes
 web/root/telnet/modules/Script.java           |  176 --
 web/root/telnet/modules/TextLabel.class       |  Bin 1975 -> 0 bytes
 web/root/telnet/modules/TextLabel.java        |  109 -
 web/root/telnet/modules/UserDialog.class      |  Bin 1313 -> 0 bytes
 web/root/telnet/modules/bsx/BSXDisplay.class  |  Bin 5672 -> 0 bytes
 web/root/telnet/modules/bsx/BSXDisplay.java   |  213 --
 web/root/telnet/modules/bsx/BSXGraphic.class  |  Bin 573 -> 0 bytes
 web/root/telnet/modules/bsx/BSXGraphic.java   |   20 -
 .../telnet/modules/bsx/BSXInputStream.class   |  Bin 1436 -> 0 bytes
 .../telnet/modules/bsx/BSXInputStream.java    |   60 -
 web/root/telnet/modules/bsx/BSXObject.class   |  Bin 440 -> 0 bytes
 web/root/telnet/modules/bsx/BSXObject.java    |   14 -
 web/root/telnet/modules/bsx/BSXPolygon.class  |  Bin 2219 -> 0 bytes
 web/root/telnet/modules/bsx/BSXPolygon.java   |   45 -
 web/root/telnet/modules/bsx/BSXScene.class    |  Bin 568 -> 0 bytes
 web/root/telnet/modules/bsx/BSXScene.java     |   17 -
 web/root/telnet/socket/StatusPeer.class       |  Bin 277 -> 0 bytes
 web/root/telnet/socket/StatusPeer.java        |   47 -
 web/root/telnet/socket/TelnetIO.class         |  Bin 9052 -> 0 bytes
 web/root/telnet/socket/TelnetIO.java          |  614 -----
 web/root/telnet/socket/TelnetWrapper.class    |  Bin 5705 -> 0 bytes
 web/root/telnet/socket/TelnetWrapper.java     |  396 ----
 .../telnet/socket/TimedOutException.class     |  Bin 434 -> 0 bytes
 web/root/telnet/socket/TimedOutException.java |   19 -
 web/root/telnet/telnet.class                  |  Bin 10936 -> 0 bytes
 web/root/telnet/telnet.java                   |  576 -----
 web/root/telnet/tools/Makefile                |   43 -
 web/root/telnet/tools/mrelayd                 |  Bin 16535 -> 0 bytes
 web/root/telnet/tools/mrelayd.c               |  566 -----
 web/root/telnet/tools/mrelayd.exe             |  Bin 39936 -> 0 bytes
 web/root/telnet/tools/proxy.class             |  Bin 3592 -> 0 bytes
 web/root/telnet/tools/proxy.java              |  225 --
 web/root/telnet/tools/redirector.class        |  Bin 1966 -> 0 bytes
 web/root/telnet/tools/relayd                  |  Bin 16896 -> 0 bytes
 web/root/telnet/tools/relayd.c                |  448 ----
 web/root/telnet/tools/relayd.exe              |  Bin 38400 -> 0 bytes
 163 files changed, 27109 deletions(-)
 delete mode 100644 web/root/telnet/BUGS
 delete mode 100644 web/root/telnet/CHANGES
 delete mode 100644 web/root/telnet/COPYING
 delete mode 100644 web/root/telnet/COPYING.LIB
 delete mode 100644 web/root/telnet/CharDisplay.html
 delete mode 100644 web/root/telnet/CharDisplayTest.class
 delete mode 100644 web/root/telnet/CharDisplayTest.java
 delete mode 100644 web/root/telnet/Documentation/Source/CharDisplayTest.html
 delete mode 100644 web/root/telnet/Documentation/Source/allclasses-frame.html
 delete mode 100644 web/root/telnet/Documentation/Source/appWrapper.html
 delete mode 100644 web/root/telnet/Documentation/Source/deprecated-list.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/CharDisplay.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/SoftFont.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/Terminal.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/TerminalHost.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/package-frame.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/package-summary.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/package-tree.html
 delete mode 100644 web/root/telnet/Documentation/Source/display/vt320.html
 delete mode 100644 web/root/telnet/Documentation/Source/help-doc.html
 delete mode 100644 web/root/telnet/Documentation/Source/images/blue-ball-small.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/blue-ball.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/class-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/constrct.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/constructor-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/constructors.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/cyan-ball-small.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/cyan-ball.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/error-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/exception-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/field_ix.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/fields.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/green-ball-small.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/green-ball.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/interface-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/magenta-ball-small.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/magenta-ball.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/method-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/methods.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/package-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/red-ball-small.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/red-ball.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/variable-index.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/variables.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/yellow-ball-small.gif
 delete mode 100644 web/root/telnet/Documentation/Source/images/yellow-ball.gif
 delete mode 100644 web/root/telnet/Documentation/Source/index-all.html
 delete mode 100644 web/root/telnet/Documentation/Source/index.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/BSXModule.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/ButtonBar.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/Module.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/MudConnector.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/Script.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/TextLabel.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/package-frame.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/package-summary.html
 delete mode 100644 web/root/telnet/Documentation/Source/modules/package-tree.html
 delete mode 100644 web/root/telnet/Documentation/Source/overview-frame.html
 delete mode 100644 web/root/telnet/Documentation/Source/overview-summary.html
 delete mode 100644 web/root/telnet/Documentation/Source/overview-tree.html
 delete mode 100644 web/root/telnet/Documentation/Source/package-list
 delete mode 100644 web/root/telnet/Documentation/Source/packages.html
 delete mode 100644 web/root/telnet/Documentation/Source/proxy.html
 delete mode 100644 web/root/telnet/Documentation/Source/serialized-form.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/StatusPeer.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/TelnetIO.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/TelnetWrapper.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/TimedOutException.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/package-frame.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/package-summary.html
 delete mode 100644 web/root/telnet/Documentation/Source/socket/package-tree.html
 delete mode 100644 web/root/telnet/Documentation/Source/stylesheet.css
 delete mode 100644 web/root/telnet/Documentation/Source/telnet.html
 delete mode 100644 web/root/telnet/Documentation/images/bin.gif
 delete mode 100644 web/root/telnet/Documentation/images/border.gif
 delete mode 100644 web/root/telnet/Documentation/images/check.gif
 delete mode 100644 web/root/telnet/Documentation/images/checkS.gif
 delete mode 100644 web/root/telnet/Documentation/images/doc.gif
 delete mode 100644 web/root/telnet/Documentation/images/flash.gif
 delete mode 100644 web/root/telnet/Documentation/images/left.gif
 delete mode 100644 web/root/telnet/Documentation/images/mail.gif
 delete mode 100644 web/root/telnet/Documentation/images/star.gif
 delete mode 100644 web/root/telnet/Documentation/images/testit.gif
 delete mode 100644 web/root/telnet/Documentation/images/user.gif
 delete mode 100644 web/root/telnet/Documentation/index.html
 delete mode 100644 web/root/telnet/Documentation/user.html
 delete mode 100644 web/root/telnet/Documentation/whatis.telnet.html
 delete mode 100644 web/root/telnet/INSTALL
 delete mode 100644 web/root/telnet/IOtest.class
 delete mode 100644 web/root/telnet/IOtest.java
 delete mode 100644 web/root/telnet/Makefile
 delete mode 100644 web/root/telnet/README
 delete mode 100644 web/root/telnet/REVISION
 delete mode 100644 web/root/telnet/TODO
 delete mode 100644 web/root/telnet/appWrapper.class
 delete mode 100644 web/root/telnet/appWrapper.java
 delete mode 100644 web/root/telnet/classes.zip
 delete mode 100644 web/root/telnet/display/CharDisplay.class
 delete mode 100644 web/root/telnet/display/CharDisplay.java
 delete mode 100644 web/root/telnet/display/SoftFont.class
 delete mode 100644 web/root/telnet/display/SoftFont.java
 delete mode 100644 web/root/telnet/display/Terminal.class
 delete mode 100644 web/root/telnet/display/Terminal.java
 delete mode 100644 web/root/telnet/display/TerminalHost.class
 delete mode 100644 web/root/telnet/display/TerminalHost.java
 delete mode 100644 web/root/telnet/display/vt320.class
 delete mode 100644 web/root/telnet/display/vt320.java
 delete mode 100644 web/root/telnet/examples/bbs.html
 delete mode 100644 web/root/telnet/examples/bbs2.html
 delete mode 100644 web/root/telnet/examples/bbs3.html
 delete mode 100644 web/root/telnet/examples/lh.html
 delete mode 100644 web/root/telnet/examples/mg.html
 delete mode 100644 web/root/telnet/examples/tapp.html
 delete mode 100644 web/root/telnet/frame.class
 delete mode 100644 web/root/telnet/frame.java
 delete mode 100644 web/root/telnet/ibmtest.html
 delete mode 100644 web/root/telnet/index.download.html
 delete mode 100644 web/root/telnet/index.ssjs
 delete mode 100644 web/root/telnet/modules/BSXModule.class
 delete mode 100644 web/root/telnet/modules/BSXModule.java
 delete mode 100644 web/root/telnet/modules/ButtonBar.class
 delete mode 100644 web/root/telnet/modules/ButtonBar.java
 delete mode 100644 web/root/telnet/modules/Module.class
 delete mode 100644 web/root/telnet/modules/Module.java
 delete mode 100644 web/root/telnet/modules/MudConnector.class
 delete mode 100644 web/root/telnet/modules/MudConnector.java
 delete mode 100644 web/root/telnet/modules/Script.class
 delete mode 100644 web/root/telnet/modules/Script.java
 delete mode 100644 web/root/telnet/modules/TextLabel.class
 delete mode 100644 web/root/telnet/modules/TextLabel.java
 delete mode 100644 web/root/telnet/modules/UserDialog.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXDisplay.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXDisplay.java
 delete mode 100644 web/root/telnet/modules/bsx/BSXGraphic.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXGraphic.java
 delete mode 100644 web/root/telnet/modules/bsx/BSXInputStream.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXInputStream.java
 delete mode 100644 web/root/telnet/modules/bsx/BSXObject.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXObject.java
 delete mode 100644 web/root/telnet/modules/bsx/BSXPolygon.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXPolygon.java
 delete mode 100644 web/root/telnet/modules/bsx/BSXScene.class
 delete mode 100644 web/root/telnet/modules/bsx/BSXScene.java
 delete mode 100644 web/root/telnet/socket/StatusPeer.class
 delete mode 100644 web/root/telnet/socket/StatusPeer.java
 delete mode 100644 web/root/telnet/socket/TelnetIO.class
 delete mode 100644 web/root/telnet/socket/TelnetIO.java
 delete mode 100644 web/root/telnet/socket/TelnetWrapper.class
 delete mode 100644 web/root/telnet/socket/TelnetWrapper.java
 delete mode 100644 web/root/telnet/socket/TimedOutException.class
 delete mode 100644 web/root/telnet/socket/TimedOutException.java
 delete mode 100644 web/root/telnet/telnet.class
 delete mode 100644 web/root/telnet/telnet.java
 delete mode 100644 web/root/telnet/tools/Makefile
 delete mode 100644 web/root/telnet/tools/mrelayd
 delete mode 100644 web/root/telnet/tools/mrelayd.c
 delete mode 100755 web/root/telnet/tools/mrelayd.exe
 delete mode 100644 web/root/telnet/tools/proxy.class
 delete mode 100644 web/root/telnet/tools/proxy.java
 delete mode 100644 web/root/telnet/tools/redirector.class
 delete mode 100644 web/root/telnet/tools/relayd
 delete mode 100644 web/root/telnet/tools/relayd.c
 delete mode 100755 web/root/telnet/tools/relayd.exe

diff --git a/web/root/telnet/BUGS b/web/root/telnet/BUGS
deleted file mode 100644
index e627e4b613..0000000000
--- a/web/root/telnet/BUGS
+++ /dev/null
@@ -1,19 +0,0 @@
-The Java(tm) Telnet Applet
-
-There are still some bugs we know of and we will draw attention to during
-development. Take a look at http://www.first.gmd.de/persons/leo/java/Telnet
-from time to time to get new versions or mail to leo@first.gmd.de to get
-notified of new versions.
-
-Report bugs to leo@first.gmd.de stating the Operating System you use plus
-the Web browser or Applet Viewer version. Attach the java console log if
-possible.
-
-Known BUGS:
-
-  * There is still a bug when you try to resize the window too much.
-      SYMPTOMS: The font/screen rssize gets switched off and the font
-                is set to default
-      WORKAROUND: None yet.
-
-  * Redraw problems on Win* platforms.
diff --git a/web/root/telnet/CHANGES b/web/root/telnet/CHANGES
deleted file mode 100644
index 9b0e4fa5bd..0000000000
--- a/web/root/telnet/CHANGES
+++ /dev/null
@@ -1,563 +0,0 @@
-1999-04-09  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/MudConnector.java: Patched to make it compilable with jdk 1.2
-
-	* modules/ButtonBar.java:
-	There was a bug, when adding an input field the button event handler was not
-	initialized. :-(
-
-1999-03-20  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	When linefeeding, we may cross from top into the scrolling region.
-	bug found & fixed by Greg Doughty <gmdoughty@lucent.com>
-
-	* display/CharDisplay.java:
-	small fixes by Greg Doughty <gmdoughty@lucent.com>
-	(drawline & brightness for bold attribute)
-
-1999-03-04  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* telnet.java: Default connection goes to the web server
-
-1999-03-03  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	why did we ignore control-Fx ? seems to have been introduced by 1.22 (Rays patches)
-
-1999-03-01  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	backspace did not backspace in some editors (ESC[P should delete at least 1 char)
-	originmode renamed to moveoutsidemargins, some places in code fixed.
-	(vi O command).
-
-1998-12-01  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: \xxxx escapes
-
-	* telnet.java: localecho = no/yes/auto (auto is default)
-
-1998-08-07  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: *** empty log message ***
-
-1998-03-18  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: patched a scrollbar bug
-
-1998-03-07  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: *** empty log message ***
-
-	* display/vt320.java: added documentation. a bit ;)
-
-	* display/vt320.java:
-	some changes and additions to function key handling (contributed by Mike Scott <MIMS@chevron.com>, cleaned up by me :)
-
-1998-02-24  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java: text is cleared after send
-
-1998-02-23  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	bugfix for unEscape, while even number of chars, it had to end with a
-	backslash :(
-
-1998-02-17  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: argl, ESC  wasn't replaced
-
-	* display/vt320.java: ARGH. unEscape did not work
-
-1998-02-09  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* socket/TelnetIO.java, telnet.java, display/CharDisplay.java:
-	*** empty log message ***
-
-	* telnet.java: download for executables
-
-1997-12-16  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* socket/TelnetIO.java, telnet.java: *** empty log message ***
-
-	* socket/TelnetIO.java, modules/BSXModule.java:
-	a new module, back from the very first version of the telnet ;-)
-
-	* display/CharDisplay.java: *** empty log message ***
-
-	* telnet.java: BSX has been added to the modules list for compiling
-
-	* modules/ButtonBar.java: *** empty log message ***
-
-1997-11-07  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: ESC [ G  added
-
-	* display/vt320.java: ESC [ d implemented
-
-1997-11-04  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java:
-	ButtonBar text input field accept the return key now!
-
-1997-11-03  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: some more originmode fixes
-
-	* display/SoftFont.java: added 0x2666 , BLACK DIAMOND?
-
-	* display/vt320.java: ESC M fixed
-	scrollregions/cursorpositioning fixed
-	didn't scroll when in last line... fixed
-
-1997-11-03  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/Script.java: Script now includes dialog popup support
-
-	* display/vt320.java: documentation for F-keys
-
-1997-10-31  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/SoftFont.java, display/vt320.java:
-	ESC [ P fixed (vanishing text in vi I think),
-	ESC [ @ added (cmdline editing bash),
-	statusline handling removed again (too broken for good),
-	misc characterset fixes
-
-1997-10-30  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/Script.java:
-	Included support for Dialog prompting, after an idea from
-	-> albert s boyers <boyers@minniemouse.cemax.com>
-
-	* display/CharDisplay.java: *** empty log message ***
-
-	* telnet.java: My version of index.html got corrupted.
-	I have removed the output of module names from telnet.java.
-
-1997-10-15  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: hmm, don't need CSI Pn $ - ?
-
-1997-10-15  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java, display/vt320.java:
-	First try on a status line.
-
-1997-10-14  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: *** empty log message ***
-
-1997-10-13  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: prelim statusline support
-
-	* display/CharDisplay.java:
-	windowBase doesn't belong into calc. of the X cursorposition, Leo!!!
-
-1997-10-10  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: scrollback buffer bug fixed
-
-1997-10-08  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java: Switched back to FlowLayout.
-
-1997-10-07  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: selectionend underflows fixed too
-
-1997-09-30  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: 8 bit CSI was wrong
-	RI added
-
-	* display/CharDisplay.java:
-	fix selection overflows when resizing on Win*
-
-1997-09-05  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: a bit more debugoutput
-
-1997-09-05  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* socket/TelnetWrapper.java: *** empty log message ***
-
-1997-09-05  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* socket/TimedOutException.java: new file by George Ruban
-
-	* socket/TelnetIO.java: George Ruban added available
-
-1997-08-30  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/SoftFont.java:
-	square root, black square added. one char fixed
-
-	* display/CharDisplay.java:
-	colors by default darker so brightness works better ;)
-
-	* display/vt320.java:
-	fixed focus problem in NS4 by using display.requestFocus instead of just requestFocus
-
-1997-08-29  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: debuyg off
-
-	* display/vt320.java:
-	report chars >0x100, where no softfont is present.
-	fixed 1 character, added DARK SHADE, BLACK SQUARE, SQUARE ROOT
-
-1997-08-18  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java: *** empty log message ***
-
-	* modules/ButtonBar.java: GridLayout error
-
-	* modules/ButtonBar.java: changing the configuration...
-
-	* display/CharDisplay.java:
-	small bugfix, setting the font only once in paint()
-
-	* telnet.java:
-	First attempts to ease module configuration (configuration file)
-
-1997-08-17  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: @ can now be generated on german keyboards
-
-1997-08-07  Marcus Meissner  <marcus@tanis.first.gmd.de>
-
-	* display/vt320.java: added origin mode (vor ESC [ .. H).
-	fixed tabsetting.
-	(two vttest problems)
-
-	* display/CharDisplay.java:
-	deleteLine fixed... was copying one line too many
-
-1997-08-02  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: gr mapping broken
-
-1997-07-25  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	basic charset handling fixed, charset G2/G3 switchers added. (still lacking)
-
-1997-07-24  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java: gridLayout for ButtonBar
-
-	* modules/ButtonBar.java: *** empty log message ***
-
-	* display/CharDisplay.java:
-	I have fixed the scrollBar bug. Expect a strange looking scrollbar under
-	Windows 95!, but it works as expected!
-
-	* telnet.java, appWrapper.java:
-	There is a bug I cannot explain, when using the "startButton" with
-	appWrapper. The telnet window will black out when iconizing the
-	browser window. However, resizing the telnet window redisplays its
-	contents.
-
-1997-07-21  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: changed bare ESC to \u001b
-
-1997-07-10  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: --debugoutput
-
-	* display/CharDisplay.java:
-	deleteLine: in case of a specified scroll area, deleteLine's bottom was 1 too less (second part of emacs ^K bug)
-
-1997-07-09  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: debug off
-
-1997-07-09  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/TextLabel.java: a simple applet to display text labels
-
-1997-07-09  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	CSI Pn M was deleting one less line than desired.... one part of emacs ^K problem fixed
-
-1997-07-08  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java, appWrapper.java:
-	reattaching applet when clicking on the space on the web browser
-
-	* modules/ButtonBar.java, appWrapper.java, frame.java, telnet.java:
-	Closing the frame of a detached window destroys it!
-
-1997-07-04  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/SoftFont.java: decreased memory usage...
-
-	* display/vt320.java:
-	use 8bit control chars only when not using ibmcharset...
-
-	* display/SoftFont.java: *** empty log message ***
-
-	* display/SoftFont.java:
-	speed up for softfonts by directly specifying fillrects (not complete)
-
-1997-07-01  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* telnet.java, appWrapper.java: *** empty log message ***
-
-	* modules/ButtonBar.java:
-	When no parent window was created, destroy applet window.
-
-	* appWrapper.java: *** empty log message ***
-
-1997-07-01  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	oops, some TSTATE_xxx weren't switching back to TSTATE_DATA
-
-1997-06-30  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* appWrapper.java: added more documentation, more changes to come
-
-1997-06-29  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* appWrapper.java:
-	The appWrapper can now only display a button to launch the applet
-
-1997-06-29  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: use 8bit control chars even when using ibmcharset
-
-	* display/SoftFont.java: *** empty log message ***
-
-	* display/vt320.java:
-	use cp431 (doslatin1us) instead of cp850 (doslatin1) ... most bbs systems
-	seem to use this.
-
-	* display/CharDisplay.java: - added softfont capability.
-	  CharDisplay is now able to display softfonts (rendered by SoftFont) and
-	  standard fonts so the loss of speed should be marginal.
-	- some cleanups in paint()
-
-	* display/SoftFont.java:
-	a softfont emulator capable to display usually missing UNICODE characters
-	(currently used for line and boxdrawings in the IBM PC charset)
-
-1997-06-09  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	don't handle chars as bit-8-set VT sequences when using the IBM charset
-
-1997-06-08  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	hmm, telnet linefeed string changed from \r\0 to \r\n (CR LF?) (RFC 1123)
-
-1997-05-27  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: there was a nullpointer bug
-
-	* tools/proxy.java: added date and time logging
-
-1997-05-27  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	a lot of additions and fixes done by "Ray Whitmer <raywhitmer@itsnet.com>"
-	- key handling (esp. function key, return etc.)
-	- Emulation fixes (a lot of them)
-	- VMS additions
-
-1997-05-27  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* socket/TelnetIO.java, display/vt320.java, appWrapper.java, telnet.java:
-	included version information for online retrieval
-
-1997-05-27  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: *** empty log message ***
-
-1997-05-27  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: documentation for setScrollbar()
-
-1997-05-26  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java, display/vt320.java:
-	moved the scrollbar to the character display
-
-1997-05-20  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: changed VTibmcharset to VTcharset
-
-	* display/vt320.java: added docu
-
-	* display/vt320.java: - scratch any \r input from terminal
-	- ibm charset mapping added
-
-1997-05-08  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	added restore default color (fore and background) in ESC [ Pn* m
-
-1997-05-06  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: fixed insertmode ESC [ 4 l/h
-
-1997-05-05  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: some more documentation
-
-	* display/CharDisplay.java:
-	implemented colors: bright + black reveals as dark gray (foreground only)
-
-	* display/CharDisplay.java, display/vt320.java:
-	added a border to the character display
-	cursorposition is now set and retrieved by setCursorPos() and getCursorPos()
-	necessary for future adaption to JDK 1.1
-
-1997-04-29  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* telnet.java:
-	return now sends \r\n to the terminal and \r\0 to the server.
-
-1997-04-18  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: ESC [ L fixed ... messed up 'vi' o command
-
-1997-04-15  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* tools/proxy.java:
-	the connecting client gets a message when the remote host is
-	not available.
-
-1997-04-14  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* telnet.java: telnet will not connect if no host is given as parameter
-
-	* tools/proxy.java: the proxy does not exit when a host is unavailable
-
-	* socket/TelnetIO.java:
-	bugfix: lost connection is now detected immediately
-
-	* modules/MudConnector.java: 3D-status bar and info Button
-
-	* display/CharDisplay.java: rudimentary support for copy&paste
-
-	* telnet.java: added compilation of MudConnector to Makefile
-
-1997-04-10  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/CharDisplay.java:
-	deleteLine ... nicht size.height, sondern bottom.
-	fixt ein 'vim' Problem
-
-1997-04-04  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* telnet.java: telnet.java does not exit when no destination is given.
-
-	* modules/ButtonBar.java, modules/MudConnector.java:
-	MudConnector is a new module designed for Andrew Cowans www.mudconnect.com
-	It loads a file of muds plus addresses to connect to. WOrks only in
-	connection with a proxy server!
-
-1997-04-04  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* socket/TelnetIO.java: SGI problem fixed.
-
-	* socket/TelnetIO.java: *** empty log message ***
-
-1997-03-24  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/Script.java, modules/ButtonBar.java, modules/Module.java:
-	module interface changed: receive() now returns a String or null
-
-	* telnet.java:
-	removed the script module from testpage and changed the module interface
-	slightly
-
-1997-03-19  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/Module.java, modules/Script.java, modules/ButtonBar.java:
-	Modules are now notified of connect() and disconnect()
-
-	* telnet.java:
-	added notification of modules upon connect and disconnect
-
-1997-03-19  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java:
-	delete left frtom cursor was leaving out 1 element
-
-	* display/vt320.java: use enough tabs....
-
-1997-03-18  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: total reformatiert auf zweier spaces
-
-1997-03-18  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: fixed a bug when resizing the screen
-
-1997-03-18  msmeissn  <msmeissn@tanis.first.gmd.de>
-
-	* display/vt320.java: layout(0 statt display.resize9)
-
-	* display/vt320.java: und nochnmal
-
-	* display/vt320.java: ESC [ nr P added
-	first try at 132/80 resize
-
-1997-03-18  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/CharDisplay.java: *** empty log message ***
-
-	* modules/ButtonBar.java:
-	It seems that \27 != ^[ (escape). Now the \e escape should work.
-
-	* display/vt320.java:
-	Don't initialize, when a character display exists.
-	This should fix the detaching problem, where the screen was blank after
-	it was added to the external frame.
-
-1997-03-17  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java: added documentation for the \e character
-
-1997-03-13  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* display/vt320.java: fixed bug in documentation VTrows default is 24
-
-1997-03-12  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/ButtonBar.java: fixed bug in send text from input field
-
-	* telnet.java: minor changes
-
-1997-03-05  Matthias L. Jugel  <leo@tanis.first.gmd.de>
-
-	* modules/Script.java, modules/ButtonBar.java, display/vt320.java:
-	added documentation
-
-	* modules/ButtonBar.java:
-	moved the documentation from index.html to this source file
-
-	* telnet.java: fixed bug in connect
-
-	* modules/ButtonBar.java: port number bug
-
-	* display/vt320.java: documentation changes
-
-	* CharDisplayTest.java, IOtest.java, appWrapper.java, display/CharDisplay.java, display/Terminal.java, display/TerminalHost.java, display/vt320.java, modules/ButtonBar.java, modules/Module.java, modules/Script.java, socket/StatusPeer.java, socket/TelnetIO.java, telnet.java, tools/proxy.java:
-	Initial CVS Registering
-
-	This is the java(tm) Telnet Applet
-
-
-	* CharDisplayTest.java, IOtest.java, appWrapper.java, display/CharDisplay.java, display/Terminal.java, display/TerminalHost.java, display/vt320.java, modules/ButtonBar.java, modules/Module.java, modules/Script.java, socket/StatusPeer.java, socket/TelnetIO.java, telnet.java, tools/proxy.java:
-	New file.
-
diff --git a/web/root/telnet/COPYING b/web/root/telnet/COPYING
deleted file mode 100644
index 32e6f4083f..0000000000
--- a/web/root/telnet/COPYING
+++ /dev/null
@@ -1,333 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-
-Version 2, June 1991
-
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.  675
-Mass Ave, Cambridge, MA 02139, USA
-
-Everyone is permitted to copy and distribute verbatim copies
-of this license document, but changing it is not allowed.
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to
-share and change it. By contrast, the GNU General Public License is intended
-to guarantee your freedom to share and change free software--to make sure
-the software is free for all its users. This General Public License applies
-to most of the Free Software Foundation's software and to any other program
-whose authors commit to using it. (Some other Free Software Foundation
-software is covered by the GNU Library General Public License instead.) You
-can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our
-General Public Licenses are designed to make sure that you have the freedom
-to distribute copies of free software (and charge for this service if you
-wish), that you receive source code or can get it if you want it, that you
-can change the software or use pieces of it in new free programs; and that
-you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to
-deny you these rights or to ask you to surrender the rights. These
-restrictions translate to certain responsibilities for you if you distribute
-copies of the software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or
-for a fee, you must give the recipients all the rights that you have. You
-must make sure that they, too, receive or can get the source code. And you
-must show them these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2)
-offer you this license which gives you legal permission to copy, distribute
-and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that
-everyone understands that there is no warranty for this free software. If
-the software is modified by someone else and passed on, we want its
-recipients to know that what they have is not the original, so that any
-problems introduced by others will not reflect on the original authors'
-reputations.
-
-Finally, any free program is threatened constantly by software patents. We
-wish to avoid the danger that redistributors of a free program will
-individually obtain patent licenses, in effect making the program
-proprietary. To prevent this, we have made it clear that any patent must be
-licensed for everyone's free use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification
-follow.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License applies to any program or other work which contains a notice
-placed by the copyright holder saying it may be distributed under the terms
-of this General Public License. The "Program", below, refers to any such
-program or work, and a "work based on the Program" means either the Program
-or any derivative work under copyright law: that is to say, a work
-containing the Program or a portion of it, either verbatim or with
-modifications and/or translated into another language. (Hereinafter,
-translation is included without limitation in the term "modification".) Each
-licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not covered
-by this License; they are outside its scope. The act of running the Program
-is not restricted, and the output from the Program is covered only if its
-contents constitute a work based on the Program (independent of having been
-made by running the Program). Whether that is true depends on what the
-Program does.
-
-1. You may copy and distribute verbatim copies of the Program's source code
-as you receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice and
-disclaimer of warranty; keep intact all the notices that refer to this
-License and to the absence of any warranty; and give any other recipients of
-the Program a copy of this License along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and you
-may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it,
-thus forming a work based on the Program, and copy and distribute such
-modifications or work under the terms of Section 1 above, provided that you
-also meet all of these conditions:
-
-   * a) You must cause the modified files to carry prominent notices stating
-     that you changed the files and the date of any change.
-
-   * b) You must cause any work that you distribute or publish, that in
-     whole or in part contains or is derived from the Program or any part
-     thereof, to be licensed as a whole at no charge to all third parties
-     under the terms of this License.
-
-   * c) If the modified program normally reads commands interactively when
-     run, you must cause it, when started running for such interactive use
-     in the most ordinary way, to print or display an announcement including
-     an appropriate copyright notice and a notice that there is no warranty
-     (or else, saying that you provide a warranty) and that users may
-     redistribute the program under these conditions, and telling the user
-     how to view a copy of this License. (Exception: if the Program itself
-     is interactive but does not normally print such an announcement, your
-     work based on the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If identifiable
-sections of that work are not derived from the Program, and can be
-reasonably considered independent and separate works in themselves, then
-this License, and its terms, do not apply to those sections when you
-distribute them as separate works. But when you distribute the same sections
-as part of a whole which is a work based on the Program, the distribution of
-the whole must be on the terms of this License, whose permissions for other
-licensees extend to the entire whole, and thus to each and every part
-regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your
-rights to work written entirely by you; rather, the intent is to exercise
-the right to control the distribution of derivative or collective works
-based on the Program.
-
-In addition, mere aggregation of another work not based on the Program with
-the Program (or with a work based on the Program) on a volume of a storage
-or distribution medium does not bring the other work under the scope of this
-License.
-
-3. You may copy and distribute the Program (or a work based on it, under
-Section 2) in object code or executable form under the terms of Sections 1
-and 2 above provided that you also do one of the following:
-
-   * a) Accompany it with the complete corresponding machine-readable source
-     code, which must be distributed under the terms of Sections 1 and 2
-     above on a medium customarily used for software interchange; or,
-
-   * b) Accompany it with a written offer, valid for at least three years,
-     to give any third party, for a charge no more than your cost of
-     physically performing source distribution, a complete machine-readable
-     copy of the corresponding source code, to be distributed under the
-     terms of Sections 1 and 2 above on a medium customarily used for
-     software interchange; or,
-
-   * c) Accompany it with the information you received as to the offer to
-     distribute corresponding source code. (This alternative is allowed only
-     for noncommercial distribution and only if you received the program in
-     object code or executable form with such an offer, in accord with
-     Subsection b above.)
-
-The source code for a work means the preferred form of the work for making
-modifications to it. For an executable work, complete source code means all
-the source code for all modules it contains, plus any associated interface
-definition files, plus the scripts used to control compilation and
-installation of the executable. However, as a special exception, the source
-code distributed need not include anything that is normally distributed (in
-either source or binary form) with the major components (compiler, kernel,
-and so on) of the operating system on which the executable runs, unless that
-component itself accompanies the executable.
-
-If distribution of executable or object code is made by offering access to
-copy from a designated place, then offering equivalent access to copy the
-source code from the same place counts as distribution of the source code,
-even though third parties are not compelled to copy the source along with
-the object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as
-expressly provided under this License. Any attempt otherwise to copy,
-modify, sublicense or distribute the Program is void, and will automatically
-terminate your rights under this License. However, parties who have received
-copies, or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-5. You are not required to accept this License, since you have not signed
-it. However, nothing else grants you permission to modify or distribute the
-Program or its derivative works. These actions are prohibited by law if you
-do not accept this License. Therefore, by modifying or distributing the
-Program (or any work based on the Program), you indicate your acceptance of
-this License to do so, and all its terms and conditions for copying,
-distributing or modifying the Program or works based on it.
-
-6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the original
-licensor to copy, distribute or modify the Program subject to these terms
-and conditions. You may not impose any further restrictions on the
-recipients' exercise of the rights granted herein. You are not responsible
-for enforcing compliance by third parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot distribute so
-as to satisfy simultaneously your obligations under this License and any
-other pertinent obligations, then as a consequence you may not distribute
-the Program at all. For example, if a patent license would not permit
-royalty-free redistribution of the Program by all those who receive copies
-directly or indirectly through you, then the only way you could satisfy both
-it and this License would be to refrain entirely from distribution of the
-Program.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply and
-the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents
-or other property right claims or to contest validity of any such claims;
-this section has the sole purpose of protecting the integrity of the free
-software distribution system, which is implemented by public license
-practices. Many people have made generous contributions to the wide range of
-software distributed through that system in reliance on consistent
-application of that system; it is up to the author/donor to decide if he or
-she is willing to distribute software through any other system and a
-licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a
-consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain
-countries either by patents or by copyrighted interfaces, the original
-copyright holder who places the Program under this License may add an
-explicit geographical distribution limitation excluding those countries, so
-that distribution is permitted only in or among countries not thus excluded.
-In such case, this License incorporates the limitation as if written in the
-body of this License.
-
-9. The Free Software Foundation may publish revised and/or new versions of
-the General Public License from time to time. Such new versions will be
-similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-10. If you wish to incorporate parts of the Program into other free programs
-whose distribution conditions are different, write to the author to ask for
-permission. For software which is copyrighted by the Free Software
-Foundation, write to the Free Software Foundation; we sometimes make
-exceptions for this. Our decision will be guided by the two goals of
-preserving the free status of all derivatives of our free software and of
-promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
-THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
-THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
-PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
-CORRECTION.
-
-12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
-LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
-THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible
-use to the public, the best way to achieve this is to make it free software
-which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to
-attach them to the start of each source file to most effectively convey the
-exclusion of warranty; and each file should have at least the "copyright"
-line and a pointer to where the full notice is found.
-
-one line to give the program's name and an idea of what it does.
-Copyright (C) 19yy  name of author
-
-This program 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.
-
-This program 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 this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this when
-it starts in an interactive mode:
-
-Gnomovision version 69, Copyright (C) 19yy name of author
-Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
-type `show w'.  This is free software, and you are welcome
-to redistribute it under certain conditions; type `show c'
-for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may be
-called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright
-interest in the program `Gnomovision'
-(which makes passes at compilers) written
-by James Hacker.
-
-signature of Ty Coon, 1 April 1989
-Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General Public
-License instead of this License.
diff --git a/web/root/telnet/COPYING.LIB b/web/root/telnet/COPYING.LIB
deleted file mode 100644
index ed7233fe00..0000000000
--- a/web/root/telnet/COPYING.LIB
+++ /dev/null
@@ -1,462 +0,0 @@
-GNU LIBRARY GENERAL PUBLIC LICENSE
-
-Version 2, June 1991
-
-Copyright (C) 1991 Free Software Foundation, Inc.
-675 Mass Ave, Cambridge, MA 02139, USA
-Everyone is permitted to copy and distribute verbatim copies
-of this license document, but changing it is not allowed.
-
-[This is the first released version of the library GPL.  It is
- numbered 2 because it goes with version 2 of the ordinary GPL.]
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to
-share and change it. By contrast, the GNU General Public Licenses are
-intended to guarantee your freedom to share and change free software--to
-make sure the software is free for all its users.
-
-This license, the Library General Public License, applies to some specially
-designated Free Software Foundation software, and to any other libraries
-whose authors decide to use it. You can use it for your libraries, too.
-
-When we speak of free software, we are referring to freedom, not price. Our
-General Public Licenses are designed to make sure that you have the freedom
-to distribute copies of free software (and charge for this service if you
-wish), that you receive source code or can get it if you want it, that you
-can change the software or use pieces of it in new free programs; and that
-you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to
-deny you these rights or to ask you to surrender the rights. These
-restrictions translate to certain responsibilities for you if you distribute
-copies of the library, or if you modify it.
-
-For example, if you distribute copies of the library, whether gratis or for
-a fee, you must give the recipients all the rights that we gave you. You
-must make sure that they, too, receive or can get the source code. If you
-link a program with the library, you must provide complete object files to
-the recipients so that they can relink them with the library, after making
-changes to the library and recompiling it. And you must show them these
-terms so they know their rights.
-
-Our method of protecting your rights has two steps: (1) copyright the
-library, and (2) offer you this license which gives you legal permission to
-copy, distribute and/or modify the library.
-
-Also, for each distributor's protection, we want to make certain that
-everyone understands that there is no warranty for this free library. If the
-library is modified by someone else and passed on, we want its recipients to
-know that what they have is not the original version, so that any problems
-introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We
-wish to avoid the danger that companies distributing free software will
-individually obtain patent licenses, thus in effect transforming the program
-into proprietary software. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-Most GNU software, including some libraries, is covered by the ordinary GNU
-General Public License, which was designed for utility programs. This
-license, the GNU Library General Public License, applies to certain
-designated libraries. This license is quite different from the ordinary one;
-be sure to read it in full, and don't assume that anything in it is the same
-as in the ordinary license.
-
-The reason we have a separate public license for some libraries is that they
-blur the distinction we usually make between modifying or adding to a
-program and simply using it. Linking a program with a library, without
-changing the library, is in some sense simply using the library, and is
-analogous to running a utility program or application program. However, in a
-textual and legal sense, the linked executable is a combined work, a
-derivative of the original library, and the ordinary General Public License
-treats it as such.
-
-Because of this blurred distinction, using the ordinary General Public
-License for libraries did not effectively promote software sharing, because
-most developers did not use the libraries. We concluded that weaker
-conditions might promote sharing better.
-
-However, unrestricted linking of non-free programs would deprive the users
-of those programs of all benefit from the free status of the libraries
-themselves. This Library General Public License is intended to permit
-developers of non-free programs to use free libraries, while preserving your
-freedom as a user of such programs to change the free libraries that are
-incorporated in them. (We have not seen how to achieve this as regards
-changes in header files, but we have achieved it as regards changes in the
-actual functions of the Library.) The hope is that this will lead to faster
-development of free libraries.
-
-The precise terms and conditions for copying, distribution and modification
-follow. Pay close attention to the difference between a "work based on the
-library" and a "work that uses the library". The former contains code
-derived from the library, while the latter only works together with the
-library.
-
-Note that it is possible for a library to be covered by the ordinary General
-Public License rather than by this special one.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any software library which contains a
-notice placed by the copyright holder or other authorized party saying it
-may be distributed under the terms of this Library General Public License
-(also called "this License"). Each licensee is addressed as "you".
-
-A "library" means a collection of software functions and/or data prepared so
-as to be conveniently linked with application programs (which use some of
-those functions and data) to form executables.
-
-The "Library", below, refers to any such software library or work which has
-been distributed under these terms. A "work based on the Library" means
-either the Library or any derivative work under copyright law: that is to
-say, a work containing the Library or a portion of it, either verbatim or
-with modifications and/or translated straightforwardly into another
-language. (Hereinafter, translation is included without limitation in the
-term "modification".)
-
-"Source code" for a work means the preferred form of the work for making
-modifications to it. For a library, complete source code means all the
-source code for all modules it contains, plus any associated interface
-definition files, plus the scripts used to control compilation and
-installation of the library.
-
-Activities other than copying, distribution and modification are not covered
-by this License; they are outside its scope. The act of running a program
-using the Library is not restricted, and output from such a program is
-covered only if its contents constitute a work based on the Library
-(independent of the use of the Library in a tool for writing it). Whether
-that is true depends on what the Library does and what the program that uses
-the Library does.
-
-1. You may copy and distribute verbatim copies of the Library's complete
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the notices
-that refer to this License and to the absence of any warranty; and
-distribute a copy of this License along with the Library.
-
-You may charge a fee for the physical act of transferring a copy, and you
-may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Library or any portion of it,
-thus forming a work based on the Library, and copy and distribute such
-modifications or work under the terms of Section 1 above, provided that you
-also meet all of these conditions:
-
-   * a) The modified work must itself be a software library.
-
-   * b) You must cause the files modified to carry prominent notices stating
-     that you changed the files and the date of any change.
-
-   * c) You must cause the whole of the work to be licensed at no charge to
-     all third parties under the terms of this License.
-
-   * d) If a facility in the modified Library refers to a function or a
-     table of data to be supplied by an application program that uses the
-     facility, other than as an argument passed when the facility is
-     invoked, then you must make a good faith effort to ensure that, in the
-     event an application does not supply such function or table, the
-     facility still operates, and performs whatever part of its purpose
-     remains meaningful. (For example, a function in a library to compute
-     square roots has a purpose that is entirely well-defined independent of
-     the application. Therefore, Subsection 2d requires that any
-     application-supplied function or table used by this function must be
-     optional: if the application does not supply it, the square root
-     function must still compute square roots.)
-
-These requirements apply to the modified work as a whole. If identifiable
-sections of that work are not derived from the Library, and can be
-reasonably considered independent and separate works in themselves, then
-this License, and its terms, do not apply to those sections when you
-distribute them as separate works. But when you distribute the same sections
-as part of a whole which is a work based on the Library, the distribution of
-the whole must be on the terms of this License, whose permissions for other
-licensees extend to the entire whole, and thus to each and every part
-regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your
-rights to work written entirely by you; rather, the intent is to exercise
-the right to control the distribution of derivative or collective works
-based on the Library.
-
-In addition, mere aggregation of another work not based on the Library with
-the Library (or with a work based on the Library) on a volume of a storage
-or distribution medium does not bring the other work under the scope of this
-License.
-
-3. You may opt to apply the terms of the ordinary GNU General Public License
-instead of this License to a given copy of the Library. To do this, you must
-alter all the notices that refer to this License, so that they refer to the
-ordinary GNU General Public License, version 2, instead of to this License.
-(If a newer version than version 2 of the ordinary GNU General Public
-License has appeared, then you can specify that version instead if you
-wish.) Do not make any other change in these notices.
-
-Once this change is made in a given copy, it is irreversible for that copy,
-so the ordinary GNU General Public License applies to all subsequent copies
-and derivative works made from that copy.
-
-This option is useful when you wish to copy part of the code of the Library
-into a program that is not a library.
-
-4. You may copy and distribute the Library (or a portion or derivative of
-it, under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you accompany it with the complete
-corresponding machine-readable source code, which must be distributed under
-the terms of Sections 1 and 2 above on a medium customarily used for
-software interchange.
-
-If distribution of object code is made by offering access to copy from a
-designated place, then offering equivalent access to copy the source code
-from the same place satisfies the requirement to distribute the source code,
-even though third parties are not compelled to copy the source along with
-the object code.
-
-5. A program that contains no derivative of any portion of the Library, but
-is designed to work with the Library by being compiled or linked with it, is
-called a "work that uses the Library". Such a work, in isolation, is not a
-derivative work of the Library, and therefore falls outside the scope of
-this License.
-
-However, linking a "work that uses the Library" with the Library creates an
-executable that is a derivative of the Library (because it contains portions
-of the Library), rather than a "work that uses the library". The executable
-is therefore covered by this License. Section 6 states terms for
-distribution of such executables.
-
-When a "work that uses the Library" uses material from a header file that is
-part of the Library, the object code for the work may be a derivative work
-of the Library even though the source code is not. Whether this is true is
-especially significant if the work can be linked without the Library, or if
-the work is itself a library. The threshold for this to be true is not
-precisely defined by law.
-
-If such an object file uses only numerical parameters, data structure
-layouts and accessors, and small macros and small inline functions (ten
-lines or less in length), then the use of the object file is unrestricted,
-regardless of whether it is legally a derivative work. (Executables
-containing this object code plus portions of the Library will still fall
-under Section 6.)
-
-Otherwise, if the work is a derivative of the Library, you may distribute
-the object code for the work under the terms of Section 6. Any executables
-containing that work also fall under Section 6, whether or not they are
-linked directly with the Library itself.
-
-6. As an exception to the Sections above, you may also compile or link a
-"work that uses the Library" with the Library to produce a work containing
-portions of the Library, and distribute that work under terms of your
-choice, provided that the terms permit modification of the work for the
-customer's own use and reverse engineering for debugging such modifications.
-
-You must give prominent notice with each copy of the work that the Library
-is used in it and that the Library and its use are covered by this License.
-You must supply a copy of this License. If the work during execution
-displays copyright notices, you must include the copyright notice for the
-Library among them, as well as a reference directing the user to the copy of
-this License. Also, you must do one of these things:
-
-   * a) Accompany the work with the complete corresponding machine-readable
-     source code for the Library including whatever changes were used in the
-     work (which must be distributed under Sections 1 and 2 above); and, if
-     the work is an executable linked with the Library, with the complete
-     machine-readable "work that uses the Library", as object code and/or
-     source code, so that the user can modify the Library and then relink to
-     produce a modified executable containing the modified Library. (It is
-     understood that the user who changes the contents of definitions files
-     in the Library will not necessarily be able to recompile the
-     application to use the modified definitions.)
-
-   * b) Accompany the work with a written offer, valid for at least three
-     years, to give the same user the materials specified in Subsection 6a,
-     above, for a charge no more than the cost of performing this
-     distribution.
-
-   * c) If distribution of the work is made by offering access to copy from
-     a designated place, offer equivalent access to copy the above specified
-     materials from the same place.
-
-   * d) Verify that the user has already received a copy of these materials
-     or that you have already sent this user a copy.
-
-For an executable, the required form of the "work that uses the Library"
-must include any data and utility programs needed for reproducing the
-executable from it. However, as a special exception, the source code
-distributed need not include anything that is normally distributed (in
-either source or binary form) with the major components (compiler, kernel,
-and so on) of the operating system on which the executable runs, unless that
-component itself accompanies the executable.
-
-It may happen that this requirement contradicts the license restrictions of
-other proprietary libraries that do not normally accompany the operating
-system. Such a contradiction means you cannot use both them and the Library
-together in an executable that you distribute.
-
-7. You may place library facilities that are a work based on the Library
-side-by-side in a single library together with other library facilities not
-covered by this License, and distribute such a combined library, provided
-that the separate distribution of the work based on the Library and of the
-other library facilities is otherwise permitted, and provided that you do
-these two things:
-
-   * a) Accompany the combined library with a copy of the same work based on
-     the Library, uncombined with any other library facilities. This must be
-     distributed under the terms of the Sections above.
-
-   * b) Give prominent notice with the combined library of the fact that
-     part of it is a work based on the Library, and explaining where to find
-     the accompanying uncombined form of the same work.
-
-8. You may not copy, modify, sublicense, link with, or distribute the
-Library except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense, link with, or distribute the Library
-is void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under this
-License will not have their licenses terminated so long as such parties
-remain in full compliance.
-
-9. You are not required to accept this License, since you have not signed
-it. However, nothing else grants you permission to modify or distribute the
-Library or its derivative works. These actions are prohibited by law if you
-do not accept this License. Therefore, by modifying or distributing the
-Library (or any work based on the Library), you indicate your acceptance of
-this License to do so, and all its terms and conditions for copying,
-distributing or modifying the Library or works based on it.
-
-10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the original
-licensor to copy, distribute, link with or modify the Library subject to
-these terms and conditions. You may not impose any further restrictions on
-the recipients' exercise of the rights granted herein. You are not
-responsible for enforcing compliance by third parties to this License.
-
-11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot distribute so
-as to satisfy simultaneously your obligations under this License and any
-other pertinent obligations, then as a consequence you may not distribute
-the Library at all. For example, if a patent license would not permit
-royalty-free redistribution of the Library by all those who receive copies
-directly or indirectly through you, then the only way you could satisfy both
-it and this License would be to refrain entirely from distribution of the
-Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents
-or other property right claims or to contest validity of any such claims;
-this section has the sole purpose of protecting the integrity of the free
-software distribution system which is implemented by public license
-practices. Many people have made generous contributions to the wide range of
-software distributed through that system in reliance on consistent
-application of that system; it is up to the author/donor to decide if he or
-she is willing to distribute software through any other system and a
-licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a
-consequence of the rest of this License.
-
-12. If the distribution and/or use of the Library is restricted in certain
-countries either by patents or by copyrighted interfaces, the original
-copyright holder who places the Library under this License may add an
-explicit geographical distribution limitation excluding those countries, so
-that distribution is permitted only in or among countries not thus excluded.
-In such case, this License incorporates the limitation as if written in the
-body of this License.
-
-13. The Free Software Foundation may publish revised and/or new versions of
-the Library General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Library does not specify a license version
-number, you may choose any version ever published by the Free Software
-Foundation.
-
-14. If you wish to incorporate parts of the Library into other free programs
-whose distribution conditions are incompatible with these, write to the
-author to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals of
-preserving the free status of all derivatives of our free software and of
-promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
-THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
-THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY
-PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
-CORRECTION.
-
-16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO
-LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
-THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER
-SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Libraries
-
-If you develop a new library, and you want it to be of the greatest possible
-use to the public, we recommend making it free software that everyone can
-redistribute and change. You can do so by permitting redistribution under
-these terms (or, alternatively, under the terms of the ordinary General
-Public License).
-
-To apply these terms, attach the following notices to the library. It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-one line to give the library's name and an idea of what it does.
-Copyright (C) year  name of author
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public
-License as published by the Free Software Foundation; either
-version 2 of the License, or (at your option) any later version.
-
-This library 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
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with this library; if not, write to the
-Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
-MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright interest in
-the library `Frob' (a library for tweaking knobs) written
-by James Random Hacker.
-
-signature of Ty Coon, 1 April 1990
-Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/web/root/telnet/CharDisplay.html b/web/root/telnet/CharDisplay.html
deleted file mode 100644
index 4bb7245cc3..0000000000
--- a/web/root/telnet/CharDisplay.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet: Test drive</TITLE>
-</HEAD>
-
-<H1>The JAVA Telnet Applet: Test drive</H1>
-
-<P>&copy; 1996, 97 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>,
-<A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A> 
-<P>
-<CENTER>
-<TABLE BORDER>
-<APPLET CODE="CharDisplayTest.class" WIDTH=600 HEIGHT=480> </APPLET>
-</TABLE>
-</HTML>
diff --git a/web/root/telnet/CharDisplayTest.class b/web/root/telnet/CharDisplayTest.class
deleted file mode 100644
index fd452d3061032f8b309f22e70227c6c688dfc676..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 4437
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

diff --git a/web/root/telnet/CharDisplayTest.java b/web/root/telnet/CharDisplayTest.java
deleted file mode 100644
index 2cfbf7704c..0000000000
--- a/web/root/telnet/CharDisplayTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * CharDisplayTest
- * --
- * $Id: CharDisplayTest.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Mon Feb 17 20:11:20 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-import java.applet.Applet;
-import java.awt.Button;
-import java.awt.Panel;
-import java.awt.Event;
-import java.awt.FlowLayout;
-import java.awt.BorderLayout;
-import java.awt.Choice;
-import java.awt.TextField;
-import java.awt.Font;
-
-import display.CharDisplay;
-
-/**
- * CharDisplayTest -- a test applet to show the display/CharDisplay features
- * --
- * @version	$Id: CharDisplayTest.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author	Matthias L. Jugel, Marcus Mei�ner
- */
-public class CharDisplayTest extends Applet
-{
-  CharDisplay display = new CharDisplay(80, 24, "Courier", 14);
-
-  Panel buttons = new Panel();
-  Button info = new Button("Information");
-  Button chars = new Button("Character Table");
-  Button attr = new Button("Attributes");
-  Choice fonts = new Choice();
-  TextField from = new TextField("0", 4);
-
-  public void init()
-  {
-    setLayout(new BorderLayout());
-    fonts.addItem("Helvetica");
-    fonts.addItem("TimesRoman");
-    fonts.addItem("Courier");
-    fonts.addItem("Dialog");
-    fonts.addItem("DialogInput");
-    fonts.addItem("ZapfDingBats");
-    fonts.addItem("default");
-    buttons.add(info);
-    buttons.add(chars);
-    buttons.add(attr);
-    buttons.add(fonts);
-    buttons.add(from);
-    add("North", buttons);
-    display.setResizeStrategy(CharDisplay.RESIZE_FONT);
-    add("Center", display);
-    Info();
-  }
-  
-  public boolean handleEvent(Event evt)
-  {
-    if(evt.target == info) { Info(); return true; }
-    if(evt.target == chars) { CharacterTable(); return true; }
-    if(evt.target == attr) { Attributes(); return true; }
-    if(evt.id == Event.ACTION_EVENT && 
-       (evt.target == fonts || evt.target == from))
-    {
-      remove(display);
-      display = new CharDisplay(80, 24, fonts.getSelectedItem(), 12);
-      add("Center", display);
-      CharacterTable();
-      layout();
-      return true;
-    }
-    return false;
-  }
-
-  private void Clear()
-  {
-    display.deleteArea(0, 0, 80, 24);
-  }
-    
-  private void Info()
-  {
-    Clear();
-    display.putString(4, 1, "CharDisplay.class Information", CharDisplay.INVERT);
-    display.putString(4, 3, "Version: "+display.version, CharDisplay.BOLD);
-    display.putString(4, 5, "This class implements several hardware features needed to implement");
-    display.putString(4, 6, "a video terminal.");
-    display.putString(4, 7, "This includes simple operations, such as putting and inserting single");
-    display.putString(4, 8, "characters or strings on the screen, character attributes and colors.");
-    display.putString(4, 9, "Special features like inserting lines, scrolling text up or down and");
-    display.putString(4,10, "defining scrollareas help implementing terminal emulations.");
-    display.redraw();
-  }
-    
-  private void CharacterTable()
-  {
-    int ch = (new Integer(from.getText())).intValue();
-    
-    Clear();
-    display.putString( 4, 1, "Character Table", CharDisplay.INVERT);
-    for(int c = 1; c < 80; c += 6)
-      for(int l = 3; l < 23; l++)
-      {
-	display.putString(c, l, ""+ch, CharDisplay.INVERT);
-	display.putChar(c+4, l, (char)ch++);
-      }
-    display.markLine(3, 20);
-    display.redraw();
-  }
-
-  private void Attributes()
-  {
-    int c = 4, l = 8;
-    
-    Clear();
-    display.putString( 4, 1, "Character attributes", CharDisplay.INVERT);
-    display.putString( 4, 3, "Normal", CharDisplay.NORMAL);
-    display.putString(22, 3, "Bold", CharDisplay.BOLD);
-    display.putString(40, 3, "Underline", CharDisplay.UNDERLINE);
-    display.putString(58, 3, "Invert", CharDisplay.INVERT);
-
-    display.putString( 4, 5, "Black", 1 << 3 | 8 << 7);
-    display.putString(13, 5, "Red", 2 << 3);
-    display.putString(22, 5, "Green", 3 << 3);
-    display.putString(31, 5, "Yellow", 4 << 3);
-    display.putString(40, 5, "Blue", 5 << 3);
-    display.putString(49, 5, "Magenta", 6 << 3);
-    display.putString(58, 5, "Cyan", 7 << 3);
-    display.putString(67, 5, "LightGray", 8 << 3);
-    
-    for(int bg = 1; bg <= 8; bg++)
-    {
-      for(int fg = 1; fg <= 8; fg++)
-      {
-	for(int a = 0; a <= 7; a++)
-	{
-	  display.putChar(c++, l, '@', (fg << 3) | (bg << 7) | a);
-	  display.redraw();
-	}
-	c++;
-      }
-      l += 2; c = 4;
-    }
-    
-  }
-}
diff --git a/web/root/telnet/Documentation/Source/CharDisplayTest.html b/web/root/telnet/Documentation/Source/CharDisplayTest.html
deleted file mode 100644
index 4baba482e6..0000000000
--- a/web/root/telnet/Documentation/Source/CharDisplayTest.html
+++ /dev/null
@@ -1,495 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:33 CEST 1999 -->
-<TITLE>
-: Class  CharDisplayTest
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="appWrapper.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="proxy.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="CharDisplayTest.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-Class  CharDisplayTest</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--java.applet.Applet
-                          |
-                          +--<B>CharDisplayTest</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>CharDisplayTest</B><DT>extends java.applet.Applet</DL>
-
-<P>
-CharDisplayTest -- a test applet to show the display/CharDisplay features
- --
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: CharDisplayTest.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="serialized-form.html#CharDisplayTest">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="CharDisplayTest.html#CharDisplayTest()">CharDisplayTest</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="CharDisplayTest.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;evt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="CharDisplayTest.html#init()">init</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.applet.Applet"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.applet.Applet</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>destroy, 
-getAppletContext, 
-getAppletInfo, 
-getAudioClip, 
-getAudioClip, 
-getCodeBase, 
-getDocumentBase, 
-getImage, 
-getImage, 
-getLocale, 
-getParameter, 
-getParameterInfo, 
-isActive, 
-newAudioClip, 
-play, 
-play, 
-resize, 
-resize, 
-setStub, 
-showStatus, 
-start, 
-stop</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Panel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Panel</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>addNotify</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="CharDisplayTest()"><!-- --></A><H3>
-CharDisplayTest</H3>
-<PRE>
-public <B>CharDisplayTest</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="init()"><!-- --></A><H3>
-init</H3>
-<PRE>
-public void <B>init</B>()</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD>init in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;evt)</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="appWrapper.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="proxy.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="CharDisplayTest.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/allclasses-frame.html b/web/root/telnet/Documentation/Source/allclasses-frame.html
deleted file mode 100644
index cc7104a4bb..0000000000
--- a/web/root/telnet/Documentation/Source/allclasses-frame.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:31 CEST 1999 -->
-<TITLE>
-All Classes
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-<FONT size="+1" ID="FrameHeadingFont">
-<B>All Classes</B></FONT>
-<BR>
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT ID="FrameItemFont"><A HREF="appWrapper.html" TARGET="classFrame">appWrapper</A>
-<BR>
-<A HREF="modules/BSXModule.html" TARGET="classFrame">BSXModule</A>
-<BR>
-<A HREF="modules/ButtonBar.html" TARGET="classFrame">ButtonBar</A>
-<BR>
-<A HREF="display/CharDisplay.html" TARGET="classFrame">CharDisplay</A>
-<BR>
-<A HREF="CharDisplayTest.html" TARGET="classFrame">CharDisplayTest</A>
-<BR>
-<A HREF="modules/Module.html" TARGET="classFrame"><I>Module</I></A>
-<BR>
-<A HREF="modules/MudConnector.html" TARGET="classFrame">MudConnector</A>
-<BR>
-<A HREF="proxy.html" TARGET="classFrame">proxy</A>
-<BR>
-<A HREF="modules/Script.html" TARGET="classFrame">Script</A>
-<BR>
-<A HREF="display/SoftFont.html" TARGET="classFrame">SoftFont</A>
-<BR>
-<A HREF="socket/StatusPeer.html" TARGET="classFrame"><I>StatusPeer</I></A>
-<BR>
-<A HREF="telnet.html" TARGET="classFrame">telnet</A>
-<BR>
-<A HREF="socket/TelnetIO.html" TARGET="classFrame">TelnetIO</A>
-<BR>
-<A HREF="socket/TelnetWrapper.html" TARGET="classFrame">TelnetWrapper</A>
-<BR>
-<A HREF="display/Terminal.html" TARGET="classFrame">Terminal</A>
-<BR>
-<A HREF="display/TerminalHost.html" TARGET="classFrame"><I>TerminalHost</I></A>
-<BR>
-<A HREF="modules/TextLabel.html" TARGET="classFrame">TextLabel</A>
-<BR>
-<A HREF="socket/TimedOutException.html" TARGET="classFrame">TimedOutException</A>
-<BR>
-<A HREF="display/vt320.html" TARGET="classFrame">vt320</A>
-<BR>
-</FONT></TD>
-</TR>
-</TABLE>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/appWrapper.html b/web/root/telnet/Documentation/Source/appWrapper.html
deleted file mode 100644
index 55eaf78969..0000000000
--- a/web/root/telnet/Documentation/Source/appWrapper.html
+++ /dev/null
@@ -1,637 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Class  appWrapper
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="CharDisplayTest.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="appWrapper.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-Class  appWrapper</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--java.applet.Applet
-                          |
-                          +--<B>appWrapper</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>appWrapper</B><DT>extends java.applet.Applet<DT>implements java.applet.AppletStub, java.lang.Runnable</DL>
-
-<P>
-The appWrapper is thought to make the applet itself independent from
- the original context. This is necessary to be able to detach the applet
- from the web browsers window without disconnecting it from events.
- Note: This applet should work with any applet without changes.
-
- <DL>
- <DT><B><PRE>&lt;PARAM NAME=&quot;applet&quot; VALUE=&quot;<I>applet</I>&quot;&gt;</PRE></B>
- <DD>Defines the applet to be loaded by the appWrapper. State the applet 
-     class name without &quot;.class&quot;!<P>
- <DT><B><PRE>&lt;PARAM NAME=&quot;startButton&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
- <DD>If this parameter is set the applet is not loaded until the user presses
-     the button. This decreases first time download delay. The <I>text</I>
-     given as value to the parameter is shown on the button. While loading
-     the applet the message "Loading ..." is shown on the button.<P>
- <DT><B><PRE>&lt;PARAM NAME=&quot;stopButton&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
- <DD>This parameter defines the button text when the applet is loaded. When 
-     pressing the button while the applet is running this causes the applet
-     window to be destroyed and the applet is stopped.<P>
- <DT><B><PRE>&lt;PARAM NAME=&quot;frameTitle&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
- <DD>The <I>frameTitle</I> is the text that is shown in the title bar of the
-     applet window.<P>
- </DL>
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: appWrapper.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel</DD>
-<DT><B>See Also: </B><DD><A HREF="serialized-form.html#appWrapper">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="appWrapper.html#appWrapper()">appWrapper</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#appletResize(int, int)">appletResize</A></B>(int&nbsp;width,
-             int&nbsp;height)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called when the applet want's to be resized.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#getAppletInfo()">getAppletInfo</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Give information about the applet.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String[][]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#getParameterInfo()">getParameterInfo</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Give information about the appWrapper and the applet loaded.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;evt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handle button events.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#init()">init</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Applet initialization.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#paint(java.awt.Graphics)">paint</A></B>(java.awt.Graphics&nbsp;g)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Write a message to the applet area.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#reshape(int, int, int, int)">reshape</A></B>(int&nbsp;x,
-        int&nbsp;y,
-        int&nbsp;w,
-        int&nbsp;h)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reshape the applet and ourself</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="appWrapper.html#run()">run</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Load the applet finally.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.applet.Applet"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.applet.Applet</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>destroy, 
-getAppletContext, 
-getAudioClip, 
-getAudioClip, 
-getCodeBase, 
-getDocumentBase, 
-getImage, 
-getImage, 
-getLocale, 
-getParameter, 
-isActive, 
-newAudioClip, 
-play, 
-play, 
-resize, 
-resize, 
-setStub, 
-showStatus, 
-start, 
-stop</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Panel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Panel</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>addNotify</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="appWrapper()"><!-- --></A><H3>
-appWrapper</H3>
-<PRE>
-public <B>appWrapper</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="init()"><!-- --></A><H3>
-init</H3>
-<PRE>
-public void <B>init</B>()</PRE>
-<DL>
-<DD>Applet initialization. We load the class giving in parameter "applet"
- and set the stub corresponding to ours. Thus we are able to give
- it access to the parameters and any applet specific context.<DD><DL>
-<DT><B>Overrides:</B><DD>init in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="run()"><!-- --></A><H3>
-run</H3>
-<PRE>
-public void <B>run</B>()</PRE>
-<DL>
-<DD>Load the applet finally. When using a button this creates a new frame
- to put the applet in.<DD><DL>
-<DT><B>Specified by: </B><DD>run in interface java.lang.Runnable</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="appletResize(int, int)"><!-- --></A><H3>
-appletResize</H3>
-<PRE>
-public void <B>appletResize</B>(int&nbsp;width,
-                         int&nbsp;height)</PRE>
-<DL>
-<DD>This method is called when the applet want's to be resized.<DD><DL>
-<DT><B>Specified by: </B><DD>appletResize in interface java.applet.AppletStub<DT><B>Parameters:</B><DD><CODE>width</CODE> - the width of the applet<DD><CODE>height</CODE> - the height of the applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getAppletInfo()"><!-- --></A><H3>
-getAppletInfo</H3>
-<PRE>
-public java.lang.String <B>getAppletInfo</B>()</PRE>
-<DL>
-<DD>Give information about the applet.<DD><DL>
-<DT><B>Overrides:</B><DD>getAppletInfo in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getParameterInfo()"><!-- --></A><H3>
-getParameterInfo</H3>
-<PRE>
-public java.lang.String[][] <B>getParameterInfo</B>()</PRE>
-<DL>
-<DD>Give information about the appWrapper and the applet loaded.<DD><DL>
-<DT><B>Overrides:</B><DD>getParameterInfo in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="paint(java.awt.Graphics)"><!-- --></A><H3>
-paint</H3>
-<PRE>
-public void <B>paint</B>(java.awt.Graphics&nbsp;g)</PRE>
-<DL>
-<DD>Write a message to the applet area.<DD><DL>
-<DT><B>Overrides:</B><DD>paint in class java.awt.Container</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="reshape(int, int, int, int)"><!-- --></A><H3>
-reshape</H3>
-<PRE>
-public void <B>reshape</B>(int&nbsp;x,
-                    int&nbsp;y,
-                    int&nbsp;w,
-                    int&nbsp;h)</PRE>
-<DL>
-<DD>reshape the applet and ourself<DD><DL>
-<DT><B>Overrides:</B><DD>reshape in class java.awt.Component</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;evt)</PRE>
-<DL>
-<DD>Handle button events. When pressed it either creates the new applet
- window or destoys it.<DD><DL>
-<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="CharDisplayTest.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="appWrapper.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/deprecated-list.html b/web/root/telnet/Documentation/Source/deprecated-list.html
deleted file mode 100644
index 3406b6aaf3..0000000000
--- a/web/root/telnet/Documentation/Source/deprecated-list.html
+++ /dev/null
@@ -1,91 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:31 CEST 1999 -->
-<TITLE>
-: Deprecated List
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Deprecated</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H2>
-<B>Deprecated API</B></H2>
-</CENTER>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Deprecated</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/CharDisplay.html b/web/root/telnet/Documentation/Source/display/CharDisplay.html
deleted file mode 100644
index 5326b15593..0000000000
--- a/web/root/telnet/Documentation/Source/display/CharDisplay.html
+++ /dev/null
@@ -1,1563 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:34 CEST 1999 -->
-<TITLE>
-: Class  CharDisplay
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="../display/SoftFont.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="CharDisplay.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-display</FONT>
-<BR>
-Class  CharDisplay</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<B>display.CharDisplay</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>CharDisplay</B><DT>extends java.awt.Panel</DL>
-
-<P>
-A simple character display.
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: CharDisplay.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../serialized-form.html#display.CharDisplay">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="field_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Field Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#BOLD">BOLD</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Make character bold.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#debug">debug</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Enable debug messages.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#INVERT">INVERT</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Invert character.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#NORMAL">NORMAL</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Make character normal.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#RESIZE_FONT">RESIZE_FONT</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Resize the font to the new screensize.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#RESIZE_NONE">RESIZE_NONE</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Do nothing when the container is resized.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#RESIZE_SCREEN">RESIZE_SCREEN</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Resize the width and height of the characterscreen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#SCROLL_DOWN">SCROLL_DOWN</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scroll down when inserting a line.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#SCROLL_UP">SCROLL_UP</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scroll up when inserting a line.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#UNDERLINE">UNDERLINE</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Underline character.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#version">version</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;If you need the runtime version, just ask this variable.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/CharDisplay.html#CharDisplay()">CharDisplay</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Create a character display with size 80x24 and Font "Courier", size 12.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/CharDisplay.html#CharDisplay(int, int)">CharDisplay</A></B>(int&nbsp;width,
-            int&nbsp;height)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Create a character display with specific size, Font is "Courier", size 12.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/CharDisplay.html#CharDisplay(int, int, java.lang.String, int)">CharDisplay</A></B>(int&nbsp;width,
-            int&nbsp;height,
-            java.lang.String&nbsp;fname,
-            int&nbsp;fsize)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Create a character display with specific size, font and font size.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/CharDisplay.html#CharDisplay(java.lang.String, int)">CharDisplay</A></B>(java.lang.String&nbsp;fname,
-            int&nbsp;fsize)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Create a character display with 80x24 and specific font and font size.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#deleteArea(int, int, int, int)">deleteArea</A></B>(int&nbsp;c,
-           int&nbsp;l,
-           int&nbsp;w,
-           int&nbsp;h)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delete a rectangular portion of the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#deleteChar(int, int)">deleteChar</A></B>(int&nbsp;c,
-           int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delete a character at a given position on the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#deleteLine(int)">deleteLine</A></B>(int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delete a line at a specific position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getAttributes(int, int)">getAttributes</A></B>(int&nbsp;c,
-              int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the attributes for the specified position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getBottomMargin()">getBottomMargin</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the bottom scroll margin.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getBufferSize()">getBufferSize</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Retrieve current scrollback buffer size.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;char</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getChar(int, int)">getChar</A></B>(int&nbsp;c,
-        int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the character at the specified position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getColumns()">getColumns</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get amount of columns on the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.awt.Dimension</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getCursorPos()">getCursorPos</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the current cursor position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getMaxBufferSize()">getMaxBufferSize</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Retrieve maximum buffer Size.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getRows()">getRows</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get amount of rows on the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getTopMargin()">getTopMargin</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the top scroll margin.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#getWindowBase()">getWindowBase</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the current window base.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;evt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handle mouse events for copy & paste</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#insertChar(int, int, char, int)">insertChar</A></B>(int&nbsp;c,
-           int&nbsp;l,
-           char&nbsp;ch,
-           int&nbsp;attributes)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insert a character at a specific position on the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#insertLine(int)">insertLine</A></B>(int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insert a blank line at a specific position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#insertLine(int, boolean)">insertLine</A></B>(int&nbsp;l,
-           boolean&nbsp;scrollDown)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insert a blank line at a specific position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#insertLine(int, int)">insertLine</A></B>(int&nbsp;l,
-           int&nbsp;n)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insert blank lines at a specific position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#insertLine(int, int, boolean)">insertLine</A></B>(int&nbsp;l,
-           int&nbsp;n,
-           boolean&nbsp;scrollDown)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insert blank lines at a specific position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.awt.Insets</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#insets()">insets</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The insets of the character display define the border.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#markLine(int, int)">markLine</A></B>(int&nbsp;l,
-         int&nbsp;n)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mark lines to be updated with redraw().</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.awt.Color</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#notbold(java.awt.Color)">notbold</A></B>(java.awt.Color&nbsp;colr)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#paint(java.awt.Graphics)">paint</A></B>(java.awt.Graphics&nbsp;g)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Paint the current screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.awt.Dimension</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#preferredSize()">preferredSize</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return the preferred Size of the character display.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#putChar(int, int, char)">putChar</A></B>(int&nbsp;c,
-        int&nbsp;l,
-        char&nbsp;ch)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put a character on the screen with normal font and outline.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#putChar(int, int, char, int)">putChar</A></B>(int&nbsp;c,
-        int&nbsp;l,
-        char&nbsp;ch,
-        int&nbsp;attributes)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put a character on the screen with specific font and outline.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#putString(int, int, java.lang.String)">putString</A></B>(int&nbsp;c,
-          int&nbsp;l,
-          java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put a String at a specific position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#putString(int, int, java.lang.String, int)">putString</A></B>(int&nbsp;c,
-          int&nbsp;l,
-          java.lang.String&nbsp;s,
-          int&nbsp;attributes)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put a String at a specific position giving all characters the same
- attributes.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#redraw()">redraw</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redraw marked lines.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#reshape(int, int, int, int)">reshape</A></B>(int&nbsp;x,
-        int&nbsp;y,
-        int&nbsp;w,
-        int&nbsp;h)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reshape character display according to resize strategy.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setBorder(int, boolean)">setBorder</A></B>(int&nbsp;thickness,
-          boolean&nbsp;raised)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the border thickness and the border type.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setBottomMargin(int)">setBottomMargin</A></B>(int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the bottom scroll margin for the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setBufferSize(int)">setBufferSize</A></B>(int&nbsp;amount)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set scrollback buffer size.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setCursorPos(int, int)">setCursorPos</A></B>(int&nbsp;c,
-             int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Puts the cursor at the specified position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setResizeStrategy(int)">setResizeStrategy</A></B>(int&nbsp;strategy)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the strategy when window is resized.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setScrollbar(java.lang.String)">setScrollbar</A></B>(java.lang.String&nbsp;position)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the scrollbar position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setTopMargin(int)">setTopMargin</A></B>(int&nbsp;l)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the top scroll margin for the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setWindowBase(int)">setWindowBase</A></B>(int&nbsp;line)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the current window base.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#setWindowSize(int, int)">setWindowSize</A></B>(int&nbsp;width,
-              int&nbsp;height)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Change the size of the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.awt.Dimension</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#size()">size</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return the real size in points of the character display.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/CharDisplay.html#update(java.awt.Graphics)">update</A></B>(java.awt.Graphics&nbsp;g)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Update the display.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Panel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Panel</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>addNotify</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paintComponents, 
-paramString, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-<A NAME="field_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Field Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="version"><!-- --></A><H3>
-version</H3>
-<PRE>
-public java.lang.String <B>version</B></PRE>
-<DL>
-<DD>If you need the runtime version, just ask this variable.</DL>
-<HR>
-
-<A NAME="debug"><!-- --></A><H3>
-debug</H3>
-<PRE>
-public static final int <B>debug</B></PRE>
-<DL>
-<DD>Enable debug messages. This is final static to prevent unused
- code to be compiled.</DL>
-<HR>
-
-<A NAME="SCROLL_UP"><!-- --></A><H3>
-SCROLL_UP</H3>
-<PRE>
-public static final boolean <B>SCROLL_UP</B></PRE>
-<DL>
-<DD>Scroll up when inserting a line.</DL>
-<HR>
-
-<A NAME="SCROLL_DOWN"><!-- --></A><H3>
-SCROLL_DOWN</H3>
-<PRE>
-public static final boolean <B>SCROLL_DOWN</B></PRE>
-<DL>
-<DD>Scroll down when inserting a line.</DL>
-<HR>
-
-<A NAME="RESIZE_NONE"><!-- --></A><H3>
-RESIZE_NONE</H3>
-<PRE>
-public static final int <B>RESIZE_NONE</B></PRE>
-<DL>
-<DD>Do nothing when the container is resized.</DL>
-<HR>
-
-<A NAME="RESIZE_SCREEN"><!-- --></A><H3>
-RESIZE_SCREEN</H3>
-<PRE>
-public static final int <B>RESIZE_SCREEN</B></PRE>
-<DL>
-<DD>Resize the width and height of the characterscreen.</DL>
-<HR>
-
-<A NAME="RESIZE_FONT"><!-- --></A><H3>
-RESIZE_FONT</H3>
-<PRE>
-public static final int <B>RESIZE_FONT</B></PRE>
-<DL>
-<DD>Resize the font to the new screensize.</DL>
-<HR>
-
-<A NAME="NORMAL"><!-- --></A><H3>
-NORMAL</H3>
-<PRE>
-public static final int <B>NORMAL</B></PRE>
-<DL>
-<DD>Make character normal.</DL>
-<HR>
-
-<A NAME="BOLD"><!-- --></A><H3>
-BOLD</H3>
-<PRE>
-public static final int <B>BOLD</B></PRE>
-<DL>
-<DD>Make character bold.</DL>
-<HR>
-
-<A NAME="UNDERLINE"><!-- --></A><H3>
-UNDERLINE</H3>
-<PRE>
-public static final int <B>UNDERLINE</B></PRE>
-<DL>
-<DD>Underline character.</DL>
-<HR>
-
-<A NAME="INVERT"><!-- --></A><H3>
-INVERT</H3>
-<PRE>
-public static final int <B>INVERT</B></PRE>
-<DL>
-<DD>Invert character.</DL>
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="CharDisplay()"><!-- --></A><H3>
-CharDisplay</H3>
-<PRE>
-public <B>CharDisplay</B>()</PRE>
-<DL>
-<DD>Create a character display with size 80x24 and Font "Courier", size 12.</DL>
-<HR>
-
-<A NAME="CharDisplay(int, int)"><!-- --></A><H3>
-CharDisplay</H3>
-<PRE>
-public <B>CharDisplay</B>(int&nbsp;width,
-                   int&nbsp;height)</PRE>
-<DL>
-<DD>Create a character display with specific size, Font is "Courier", size 12.</DL>
-<HR>
-
-<A NAME="CharDisplay(java.lang.String, int)"><!-- --></A><H3>
-CharDisplay</H3>
-<PRE>
-public <B>CharDisplay</B>(java.lang.String&nbsp;fname,
-                   int&nbsp;fsize)</PRE>
-<DL>
-<DD>Create a character display with 80x24 and specific font and font size.</DL>
-<HR>
-
-<A NAME="CharDisplay(int, int, java.lang.String, int)"><!-- --></A><H3>
-CharDisplay</H3>
-<PRE>
-public <B>CharDisplay</B>(int&nbsp;width,
-                   int&nbsp;height,
-                   java.lang.String&nbsp;fname,
-                   int&nbsp;fsize)</PRE>
-<DL>
-<DD>Create a character display with specific size, font and font size.</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="notbold(java.awt.Color)"><!-- --></A><H3>
-notbold</H3>
-<PRE>
-public java.awt.Color <B>notbold</B>(java.awt.Color&nbsp;colr)</PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="putChar(int, int, char)"><!-- --></A><H3>
-putChar</H3>
-<PRE>
-public void <B>putChar</B>(int&nbsp;c,
-                    int&nbsp;l,
-                    char&nbsp;ch)</PRE>
-<DL>
-<DD>Put a character on the screen with normal font and outline.
- The character previously on that position will be overwritten.
- You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DD><CODE>ch</CODE> - the character to show on the screen<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#insertChar(int, int, char, int)"><CODE>insertChar(int, int, char, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#deleteChar(int, int)"><CODE>deleteChar(int, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putChar(int, int, char, int)"><!-- --></A><H3>
-putChar</H3>
-<PRE>
-public void <B>putChar</B>(int&nbsp;c,
-                    int&nbsp;l,
-                    char&nbsp;ch,
-                    int&nbsp;attributes)</PRE>
-<DL>
-<DD>Put a character on the screen with specific font and outline.
- The character previously on that position will be overwritten.
- You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DD><CODE>ch</CODE> - the character to show on the screen<DD><CODE>attributes</CODE> - the character attributes<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#BOLD"><CODE>BOLD</CODE></A>, 
-<A HREF="../display/CharDisplay.html#UNDERLINE"><CODE>UNDERLINE</CODE></A>, 
-<A HREF="../display/CharDisplay.html#INVERT"><CODE>INVERT</CODE></A>, 
-<A HREF="../display/CharDisplay.html#NORMAL"><CODE>NORMAL</CODE></A>, 
-<A HREF="../display/CharDisplay.html#insertChar(int, int, char, int)"><CODE>insertChar(int, int, char, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#deleteChar(int, int)"><CODE>deleteChar(int, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getChar(int, int)"><!-- --></A><H3>
-getChar</H3>
-<PRE>
-public char <B>getChar</B>(int&nbsp;c,
-                    int&nbsp;l)</PRE>
-<DL>
-<DD>Get the character at the specified position.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#putChar(int, int, char)"><CODE>putChar(int, int, char)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getAttributes(int, int)"><!-- --></A><H3>
-getAttributes</H3>
-<PRE>
-public int <B>getAttributes</B>(int&nbsp;c,
-                         int&nbsp;l)</PRE>
-<DL>
-<DD>Get the attributes for the specified position.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#putChar(int, int, char)"><CODE>putChar(int, int, char)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="insertChar(int, int, char, int)"><!-- --></A><H3>
-insertChar</H3>
-<PRE>
-public void <B>insertChar</B>(int&nbsp;c,
-                       int&nbsp;l,
-                       char&nbsp;ch,
-                       int&nbsp;attributes)</PRE>
-<DL>
-<DD>Insert a character at a specific position on the screen.
- All character right to from this position will be moved one to the right.
- You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DD><CODE>ch</CODE> - the character to insert<DD><CODE>attributes</CODE> - the character attributes<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#BOLD"><CODE>BOLD</CODE></A>, 
-<A HREF="../display/CharDisplay.html#UNDERLINE"><CODE>UNDERLINE</CODE></A>, 
-<A HREF="../display/CharDisplay.html#INVERT"><CODE>INVERT</CODE></A>, 
-<A HREF="../display/CharDisplay.html#NORMAL"><CODE>NORMAL</CODE></A>, 
-<A HREF="../display/CharDisplay.html#putChar(int, int, char)"><CODE>putChar(int, int, char)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#deleteChar(int, int)"><CODE>deleteChar(int, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="deleteChar(int, int)"><!-- --></A><H3>
-deleteChar</H3>
-<PRE>
-public void <B>deleteChar</B>(int&nbsp;c,
-                       int&nbsp;l)</PRE>
-<DL>
-<DD>Delete a character at a given position on the screen.
- All characters right to the position will be moved one to the left.
- You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#putChar(int, int, char)"><CODE>putChar(int, int, char)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#insertChar(int, int, char, int)"><CODE>insertChar(int, int, char, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putString(int, int, java.lang.String)"><!-- --></A><H3>
-putString</H3>
-<PRE>
-public void <B>putString</B>(int&nbsp;c,
-                      int&nbsp;l,
-                      java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Put a String at a specific position. Any characters previously on that 
- position will be overwritten. You need to call redraw() for screen update.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DD><CODE>s</CODE> - the string to be shown on the screen<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#BOLD"><CODE>BOLD</CODE></A>, 
-<A HREF="../display/CharDisplay.html#UNDERLINE"><CODE>UNDERLINE</CODE></A>, 
-<A HREF="../display/CharDisplay.html#INVERT"><CODE>INVERT</CODE></A>, 
-<A HREF="../display/CharDisplay.html#NORMAL"><CODE>NORMAL</CODE></A>, 
-<A HREF="../display/CharDisplay.html#putChar(int, int, char)"><CODE>putChar(int, int, char)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#insertLine(int)"><CODE>insertLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putString(int, int, java.lang.String, int)"><!-- --></A><H3>
-putString</H3>
-<PRE>
-public void <B>putString</B>(int&nbsp;c,
-                      int&nbsp;l,
-                      java.lang.String&nbsp;s,
-                      int&nbsp;attributes)</PRE>
-<DL>
-<DD>Put a String at a specific position giving all characters the same
- attributes. Any characters previously on that position will be 
- overwritten. You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (line)<DD><CODE>s</CODE> - the string to be shown on the screen<DD><CODE>attributes</CODE> - character attributes<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#BOLD"><CODE>BOLD</CODE></A>, 
-<A HREF="../display/CharDisplay.html#UNDERLINE"><CODE>UNDERLINE</CODE></A>, 
-<A HREF="../display/CharDisplay.html#INVERT"><CODE>INVERT</CODE></A>, 
-<A HREF="../display/CharDisplay.html#NORMAL"><CODE>NORMAL</CODE></A>, 
-<A HREF="../display/CharDisplay.html#putChar(int, int, char)"><CODE>putChar(int, int, char)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#insertLine(int)"><CODE>insertLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="insertLine(int)"><!-- --></A><H3>
-insertLine</H3>
-<PRE>
-public void <B>insertLine</B>(int&nbsp;l)</PRE>
-<DL>
-<DD>Insert a blank line at a specific position.
- The current line and all previous lines are scrolled one line up. The
- top line is lost. You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - the y-coordinate to insert the line<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="insertLine(int, int)"><!-- --></A><H3>
-insertLine</H3>
-<PRE>
-public void <B>insertLine</B>(int&nbsp;l,
-                       int&nbsp;n)</PRE>
-<DL>
-<DD>Insert blank lines at a specific position.
- You need to call redraw() to update the screen<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - the y-coordinate to insert the line<DD><CODE>n</CODE> - amount of lines to be inserted<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="insertLine(int, boolean)"><!-- --></A><H3>
-insertLine</H3>
-<PRE>
-public void <B>insertLine</B>(int&nbsp;l,
-                       boolean&nbsp;scrollDown)</PRE>
-<DL>
-<DD>Insert a blank line at a specific position. Scroll text according to
- the argument.
- You need to call redraw() to update the screen<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - the y-coordinate to insert the line<DD><CODE>scrollDown</CODE> - scroll down<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#SCROLL_UP"><CODE>SCROLL_UP</CODE></A>, 
-<A HREF="../display/CharDisplay.html#SCROLL_DOWN"><CODE>SCROLL_DOWN</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="insertLine(int, int, boolean)"><!-- --></A><H3>
-insertLine</H3>
-<PRE>
-public void <B>insertLine</B>(int&nbsp;l,
-                       int&nbsp;n,
-                       boolean&nbsp;scrollDown)</PRE>
-<DL>
-<DD>Insert blank lines at a specific position.
- The current line and all previous lines are scrolled one line up. The
- top line is lost. You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - the y-coordinate to insert the line<DD><CODE>n</CODE> - number of lines to be inserted<DD><CODE>scrollDown</CODE> - scroll down<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#SCROLL_UP"><CODE>SCROLL_UP</CODE></A>, 
-<A HREF="../display/CharDisplay.html#SCROLL_DOWN"><CODE>SCROLL_DOWN</CODE></A>, 
-<A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="deleteLine(int)"><!-- --></A><H3>
-deleteLine</H3>
-<PRE>
-public void <B>deleteLine</B>(int&nbsp;l)</PRE>
-<DL>
-<DD>Delete a line at a specific position. Subsequent lines will be scrolled 
- up to fill the space and a blank line is inserted at the end of the 
- screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - the y-coordinate to insert the line<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="deleteArea(int, int, int, int)"><!-- --></A><H3>
-deleteArea</H3>
-<PRE>
-public void <B>deleteArea</B>(int&nbsp;c,
-                       int&nbsp;l,
-                       int&nbsp;w,
-                       int&nbsp;h)</PRE>
-<DL>
-<DD>Delete a rectangular portion of the screen.
- You need to call redraw() to update the screen.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - x-coordinate (column)<DD><CODE>l</CODE> - y-coordinate (row)<DD><CODE>w</CODE> - with of the area in characters<DD><CODE>h</CODE> - height of the area in characters<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#deleteChar(int, int)"><CODE>deleteChar(int, int)</CODE></A>, 
-<A HREF="../display/CharDisplay.html#deleteLine(int)"><CODE>deleteLine(int)</CODE></A>, 
-<CODE>redraw</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setCursorPos(int, int)"><!-- --></A><H3>
-setCursorPos</H3>
-<PRE>
-public void <B>setCursorPos</B>(int&nbsp;c,
-                         int&nbsp;l)</PRE>
-<DL>
-<DD>Puts the cursor at the specified position.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - column<DD><CODE>l</CODE> - line</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getCursorPos()"><!-- --></A><H3>
-getCursorPos</H3>
-<PRE>
-public java.awt.Dimension <B>getCursorPos</B>()</PRE>
-<DL>
-<DD>Get the current cursor position.<DD><DL>
-<DT><B>See Also: </B><DD><CODE>Dimension</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setTopMargin(int)"><!-- --></A><H3>
-setTopMargin</H3>
-<PRE>
-public void <B>setTopMargin</B>(int&nbsp;l)</PRE>
-<DL>
-<DD>Set the top scroll margin for the screen. If the current bottom margin
- is smaller it will become the top margin and the line will become the
- bottom margin.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - line that is the margin</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getTopMargin()"><!-- --></A><H3>
-getTopMargin</H3>
-<PRE>
-public int <B>getTopMargin</B>()</PRE>
-<DL>
-<DD>Get the top scroll margin.</DL>
-<HR>
-
-<A NAME="setBottomMargin(int)"><!-- --></A><H3>
-setBottomMargin</H3>
-<PRE>
-public void <B>setBottomMargin</B>(int&nbsp;l)</PRE>
-<DL>
-<DD>Set the bottom scroll margin for the screen. If the current top margin
- is bigger it will become the bottom margin and the line will become the
- top margin.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - line that is the margin</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getBottomMargin()"><!-- --></A><H3>
-getBottomMargin</H3>
-<PRE>
-public int <B>getBottomMargin</B>()</PRE>
-<DL>
-<DD>Get the bottom scroll margin.</DL>
-<HR>
-
-<A NAME="setBufferSize(int)"><!-- --></A><H3>
-setBufferSize</H3>
-<PRE>
-public void <B>setBufferSize</B>(int&nbsp;amount)</PRE>
-<DL>
-<DD>Set scrollback buffer size.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>amount</CODE> - new size of the buffer</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getBufferSize()"><!-- --></A><H3>
-getBufferSize</H3>
-<PRE>
-public int <B>getBufferSize</B>()</PRE>
-<DL>
-<DD>Retrieve current scrollback buffer size.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#setBufferSize(int)"><CODE>setBufferSize(int)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getMaxBufferSize()"><!-- --></A><H3>
-getMaxBufferSize</H3>
-<PRE>
-public int <B>getMaxBufferSize</B>()</PRE>
-<DL>
-<DD>Retrieve maximum buffer Size.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#getBufferSize()"><CODE>getBufferSize()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setWindowBase(int)"><!-- --></A><H3>
-setWindowBase</H3>
-<PRE>
-public void <B>setWindowBase</B>(int&nbsp;line)</PRE>
-<DL>
-<DD>Set the current window base. This allows to view the scrollback buffer.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>line</CODE> - the line where the screen window starts<DT><B>See Also: </B><DD><CODE>setBufferSize</CODE>, 
-<CODE>getBufferSize</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getWindowBase()"><!-- --></A><H3>
-getWindowBase</H3>
-<PRE>
-public int <B>getWindowBase</B>()</PRE>
-<DL>
-<DD>Get the current window base.<DD><DL>
-<DT><B>See Also: </B><DD><CODE>setWindowBase</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setWindowSize(int, int)"><!-- --></A><H3>
-setWindowSize</H3>
-<PRE>
-public void <B>setWindowSize</B>(int&nbsp;width,
-                          int&nbsp;height)</PRE>
-<DL>
-<DD>Change the size of the screen. This will include adjustment of the 
- scrollback buffer.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>columns</CODE> - width of the screen<DD><CODE>columns</CODE> - height of the screen</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setResizeStrategy(int)"><!-- --></A><H3>
-setResizeStrategy</H3>
-<PRE>
-public void <B>setResizeStrategy</B>(int&nbsp;strategy)</PRE>
-<DL>
-<DD>Set the strategy when window is resized.
- RESIZE_FONT is default.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>strategy</CODE> - the strategy<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#RESIZE_NONE"><CODE>RESIZE_NONE</CODE></A>, 
-<A HREF="../display/CharDisplay.html#RESIZE_FONT"><CODE>RESIZE_FONT</CODE></A>, 
-<A HREF="../display/CharDisplay.html#RESIZE_SCREEN"><CODE>RESIZE_SCREEN</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getRows()"><!-- --></A><H3>
-getRows</H3>
-<PRE>
-public int <B>getRows</B>()</PRE>
-<DL>
-<DD>Get amount of rows on the screen.</DL>
-<HR>
-
-<A NAME="getColumns()"><!-- --></A><H3>
-getColumns</H3>
-<PRE>
-public int <B>getColumns</B>()</PRE>
-<DL>
-<DD>Get amount of columns on the screen.</DL>
-<HR>
-
-<A NAME="setBorder(int, boolean)"><!-- --></A><H3>
-setBorder</H3>
-<PRE>
-public void <B>setBorder</B>(int&nbsp;thickness,
-                      boolean&nbsp;raised)</PRE>
-<DL>
-<DD>Set the border thickness and the border type.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>thickness</CODE> - border thickness in pixels, zero means no border<DD><CODE>raised</CODE> - a boolean indicating a raised or embossed border</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setScrollbar(java.lang.String)"><!-- --></A><H3>
-setScrollbar</H3>
-<PRE>
-public void <B>setScrollbar</B>(java.lang.String&nbsp;position)</PRE>
-<DL>
-<DD>Set the scrollbar position. valid values are "East" or "West".<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>position</CODE> - the position of the scrollbar</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="markLine(int, int)"><!-- --></A><H3>
-markLine</H3>
-<PRE>
-public void <B>markLine</B>(int&nbsp;l,
-                     int&nbsp;n)</PRE>
-<DL>
-<DD>Mark lines to be updated with redraw().<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>l</CODE> - starting line<DD><CODE>n</CODE> - amount of lines to be updated<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#redraw()"><CODE>redraw()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="redraw()"><!-- --></A><H3>
-redraw</H3>
-<PRE>
-public void <B>redraw</B>()</PRE>
-<DL>
-<DD>Redraw marked lines.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#markLine(int, int)"><CODE>markLine(int, int)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="update(java.awt.Graphics)"><!-- --></A><H3>
-update</H3>
-<PRE>
-public void <B>update</B>(java.awt.Graphics&nbsp;g)</PRE>
-<DL>
-<DD>Update the display. to reduce flashing we have overridden this method.<DD><DL>
-<DT><B>Overrides:</B><DD>update in class java.awt.Container</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="paint(java.awt.Graphics)"><!-- --></A><H3>
-paint</H3>
-<PRE>
-public void <B>paint</B>(java.awt.Graphics&nbsp;g)</PRE>
-<DL>
-<DD>Paint the current screen. All painting is done here. Only lines that have
- changed will be redrawn!<DD><DL>
-<DT><B>Overrides:</B><DD>paint in class java.awt.Container</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="reshape(int, int, int, int)"><!-- --></A><H3>
-reshape</H3>
-<PRE>
-public void <B>reshape</B>(int&nbsp;x,
-                    int&nbsp;y,
-                    int&nbsp;w,
-                    int&nbsp;h)</PRE>
-<DL>
-<DD>Reshape character display according to resize strategy.<DD><DL>
-<DT><B>Overrides:</B><DD>reshape in class java.awt.Component<DT><B>See Also: </B><DD><A HREF="../display/CharDisplay.html#setResizeStrategy(int)"><CODE>setResizeStrategy(int)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="size()"><!-- --></A><H3>
-size</H3>
-<PRE>
-public java.awt.Dimension <B>size</B>()</PRE>
-<DL>
-<DD>Return the real size in points of the character display.<DD><DL>
-<DT><B>Returns:</B><DD>Dimension the dimension of the display<DT><B>Overrides:</B><DD>size in class java.awt.Component<DT><B>See Also: </B><DD><CODE>Dimension</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="preferredSize()"><!-- --></A><H3>
-preferredSize</H3>
-<PRE>
-public java.awt.Dimension <B>preferredSize</B>()</PRE>
-<DL>
-<DD>Return the preferred Size of the character display.
- This turns out to be the actual size.<DD><DL>
-<DT><B>Returns:</B><DD>Dimension dimension of the display<DT><B>Overrides:</B><DD>preferredSize in class java.awt.Container<DT><B>See Also: </B><DD><CODE>size</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="insets()"><!-- --></A><H3>
-insets</H3>
-<PRE>
-public java.awt.Insets <B>insets</B>()</PRE>
-<DL>
-<DD>The insets of the character display define the border.<DD><DL>
-<DT><B>Returns:</B><DD>Insets border thickness in pixels<DT><B>Overrides:</B><DD>insets in class java.awt.Container</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;evt)</PRE>
-<DL>
-<DD>Handle mouse events for copy & paste<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>evt</CODE> - the event that occured<DT><B>Returns:</B><DD>boolean true if action was taken<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component<DT><B>See Also: </B><DD><CODE>Event</CODE></DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="../display/SoftFont.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="CharDisplay.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/SoftFont.html b/web/root/telnet/Documentation/Source/display/SoftFont.html
deleted file mode 100644
index e9d22339d6..0000000000
--- a/web/root/telnet/Documentation/Source/display/SoftFont.html
+++ /dev/null
@@ -1,242 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:34 CEST 1999 -->
-<TITLE>
-: Class  SoftFont
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/CharDisplay.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../display/Terminal.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="SoftFont.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-display</FONT>
-<BR>
-Class  SoftFont</H2>
-<PRE>
-java.lang.Object
-  |
-  +--<B>display.SoftFont</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>SoftFont</B><DT>extends java.lang.Object</DL>
-
-<P>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/SoftFont.html#SoftFont()">SoftFont</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/SoftFont.html#drawChar(java.awt.Graphics, char, int, int, int, int)">drawChar</A></B>(java.awt.Graphics&nbsp;g,
-         char&nbsp;c,
-         int&nbsp;x,
-         int&nbsp;y,
-         int&nbsp;cw,
-         int&nbsp;ch)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/SoftFont.html#inSoftFont(char)">inSoftFont</A></B>(char&nbsp;c)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-toString, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="SoftFont()"><!-- --></A><H3>
-SoftFont</H3>
-<PRE>
-public <B>SoftFont</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="inSoftFont(char)"><!-- --></A><H3>
-inSoftFont</H3>
-<PRE>
-public boolean <B>inSoftFont</B>(char&nbsp;c)</PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="drawChar(java.awt.Graphics, char, int, int, int, int)"><!-- --></A><H3>
-drawChar</H3>
-<PRE>
-public void <B>drawChar</B>(java.awt.Graphics&nbsp;g,
-                     char&nbsp;c,
-                     int&nbsp;x,
-                     int&nbsp;y,
-                     int&nbsp;cw,
-                     int&nbsp;ch)</PRE>
-<DL>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/CharDisplay.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../display/Terminal.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="SoftFont.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/Terminal.html b/web/root/telnet/Documentation/Source/display/Terminal.html
deleted file mode 100644
index 842c5de1f2..0000000000
--- a/web/root/telnet/Documentation/Source/display/Terminal.html
+++ /dev/null
@@ -1,528 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:34 CEST 1999 -->
-<TITLE>
-: Class  Terminal
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/SoftFont.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../display/vt320.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="Terminal.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-display</FONT>
-<BR>
-Class  Terminal</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<B>display.Terminal</B>
-</PRE>
-<DL>
-<DT><B>Direct Known Subclasses:</B> <DD><A HREF="../display/vt320.html">vt320</A></DD>
-</DL>
-<HR>
-<DL>
-<DT>public abstract class <B>Terminal</B><DT>extends java.awt.Panel</DL>
-
-<P>
-Terminal is an abstract emulation class.
- It contains a character display.
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: Terminal.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../serialized-form.html#display.Terminal">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/Terminal.html#Terminal()">Terminal</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>abstract &nbsp;java.lang.String[][]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/Terminal.html#getParameterInfo()">getParameterInfo</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the specific parameter info for the emulation.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>abstract &nbsp;java.awt.Dimension</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/Terminal.html#getSize()">getSize</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return the current size of the terminal in characters.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>abstract &nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/Terminal.html#getTerminalType()">getTerminalType</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return actual terminal type identifier.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>abstract &nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/Terminal.html#putChar(char)">putChar</A></B>(char&nbsp;c)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put a character on the screen.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>abstract &nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/Terminal.html#putString(java.lang.String)">putString</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put a character on the screen.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Panel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Panel</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>addNotify</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-handleEvent, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="Terminal()"><!-- --></A><H3>
-Terminal</H3>
-<PRE>
-public <B>Terminal</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="getParameterInfo()"><!-- --></A><H3>
-getParameterInfo</H3>
-<PRE>
-public abstract java.lang.String[][] <B>getParameterInfo</B>()</PRE>
-<DL>
-<DD>Get the specific parameter info for the emulation.<DD><DL>
-<DT><B>See Also: </B><DD><CODE>Applet</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putChar(char)"><!-- --></A><H3>
-putChar</H3>
-<PRE>
-public abstract void <B>putChar</B>(char&nbsp;c)</PRE>
-<DL>
-<DD>Put a character on the screen. The method has to see if it is
- a special character that needs to be handles special.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>c</CODE> - the character<DT><B>See Also: </B><DD><A HREF="../display/Terminal.html#putString(java.lang.String)"><CODE>putString(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putString(java.lang.String)"><!-- --></A><H3>
-putString</H3>
-<PRE>
-public abstract void <B>putString</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Put a character on the screen. The method has to parse the string
- may handle special characters.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>s</CODE> - the string<DT><B>See Also: </B><DD><A HREF="../display/Terminal.html#putString(java.lang.String)"><CODE>putString(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getSize()"><!-- --></A><H3>
-getSize</H3>
-<PRE>
-public abstract java.awt.Dimension <B>getSize</B>()</PRE>
-<DL>
-<DD>Return the current size of the terminal in characters.<DD><DL>
-<DT><B>Overrides:</B><DD>getSize in class java.awt.Component</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getTerminalType()"><!-- --></A><H3>
-getTerminalType</H3>
-<PRE>
-public abstract java.lang.String <B>getTerminalType</B>()</PRE>
-<DL>
-<DD>Return actual terminal type identifier.</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/SoftFont.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../display/vt320.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="Terminal.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/TerminalHost.html b/web/root/telnet/Documentation/Source/display/TerminalHost.html
deleted file mode 100644
index defad23341..0000000000
--- a/web/root/telnet/Documentation/Source/display/TerminalHost.html
+++ /dev/null
@@ -1,178 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:33 CEST 1999 -->
-<TITLE>
-: Interface  TerminalHost
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TerminalHost.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-display</FONT>
-<BR>
-Interface  TerminalHost</H2>
-<DL>
-<DT><B>All Known Implementing Classes:</B> <DD><A HREF="../telnet.html">telnet</A>, <A HREF="../display/vt320.html">vt320</A></DD>
-</DL>
-<HR>
-<DL>
-<DT>public abstract interface <B>TerminalHost</B></DL>
-
-<P>
-TerminalHost is an interface for the remote (virtual) end of our connection
- to the host computer we are connected to.
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: TerminalHost.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L Jugel, Marcus Mei�ner</DD>
-</DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/TerminalHost.html#send(java.lang.String)">send</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Send a string to the host and return if it was received successfully.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="send(java.lang.String)"><!-- --></A><H3>
-send</H3>
-<PRE>
-public boolean <B>send</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Send a string to the host and return if it was received successfully.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>s</CODE> - the string to send<DT><B>Returns:</B><DD>True for successful receivement.</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TerminalHost.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/package-frame.html b/web/root/telnet/Documentation/Source/display/package-frame.html
deleted file mode 100644
index 3197fdeddd..0000000000
--- a/web/root/telnet/Documentation/Source/display/package-frame.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Package display
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-<FONT size="+1" ID="FrameTitleFont">
-<A HREF="../display/package-summary.html" TARGET="classFrame">display</A></FONT>
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Interfaces</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="TerminalHost.html" TARGET="classFrame"><I>TerminalHost</I></A></FONT></TD>
-</TR>
-</TABLE>
-
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Classes</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="CharDisplay.html" TARGET="classFrame">CharDisplay</A>
-<BR>
-<A HREF="SoftFont.html" TARGET="classFrame">SoftFont</A>
-<BR>
-<A HREF="Terminal.html" TARGET="classFrame">Terminal</A>
-<BR>
-<A HREF="vt320.html" TARGET="classFrame">vt320</A></FONT></TD>
-</TR>
-</TABLE>
-
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/package-summary.html b/web/root/telnet/Documentation/Source/display/package-summary.html
deleted file mode 100644
index 9ba0b65d51..0000000000
--- a/web/root/telnet/Documentation/Source/display/package-summary.html
+++ /dev/null
@@ -1,131 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Package display
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV PACKAGE&nbsp;
-&nbsp;<A HREF="../modules/package-summary.html"><B>NEXT PACKAGE</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<H2>
-Package display
-</H2>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Interface Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="TerminalHost.html"><I>TerminalHost</I></A></B></TD>
-<TD>TerminalHost is an interface for the remote (virtual) end of our connection
- to the host computer we are connected to.</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="CharDisplay.html">CharDisplay</A></B></TD>
-<TD>A simple character display.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="SoftFont.html">SoftFont</A></B></TD>
-<TD>&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="Terminal.html">Terminal</A></B></TD>
-<TD>Terminal is an abstract emulation class.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="vt320.html">vt320</A></B></TD>
-<TD>A DEC VT320 Terminal Emulation (includes VT100/220 and ANSI).</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV PACKAGE&nbsp;
-&nbsp;<A HREF="../modules/package-summary.html"><B>NEXT PACKAGE</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/package-tree.html b/web/root/telnet/Documentation/Source/display/package-tree.html
deleted file mode 100644
index fdec70375e..0000000000
--- a/web/root/telnet/Documentation/Source/display/package-tree.html
+++ /dev/null
@@ -1,117 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: display Class Hierarchy
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;<A HREF="../modules/package-tree.html"><B>NEXT</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H2>
-Hierarchy For Package display
-</H2>
-</CENTER>
-<DL>
-<DT><B>Package Hierarchies: </B><DD><A HREF="../overview-tree.html">All Packages</A></DL>
-<HR>
-<H2>
-Class Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">class java.lang.Object<UL>
-<LI TYPE="circle">class java.awt.Component (implements java.awt.image.ImageObserver, java.awt.MenuContainer, java.io.Serializable)
-<UL>
-<LI TYPE="circle">class java.awt.Container<UL>
-<LI TYPE="circle">class java.awt.Panel<UL>
-<LI TYPE="circle">class display.<A HREF="../display/CharDisplay.html"><B>CharDisplay</B></A><LI TYPE="circle">class display.<A HREF="../display/Terminal.html"><B>Terminal</B></A><UL>
-<LI TYPE="circle">class display.<A HREF="../display/vt320.html"><B>vt320</B></A> (implements display.<A HREF="../display/TerminalHost.html">TerminalHost</A>)
-</UL>
-</UL>
-</UL>
-</UL>
-<LI TYPE="circle">class display.<A HREF="../display/SoftFont.html"><B>SoftFont</B></A></UL>
-</UL>
-<H2>
-Interface Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">interface display.<A HREF="../display/TerminalHost.html"><B>TerminalHost</B></A></UL>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;<A HREF="../modules/package-tree.html"><B>NEXT</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/display/vt320.html b/web/root/telnet/Documentation/Source/display/vt320.html
deleted file mode 100644
index c9e5000eb1..0000000000
--- a/web/root/telnet/Documentation/Source/display/vt320.html
+++ /dev/null
@@ -1,842 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:34 CEST 1999 -->
-<TITLE>
-: Class  vt320
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/Terminal.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="vt320.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-display</FONT>
-<BR>
-Class  vt320</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<A HREF="../display/Terminal.html">display.Terminal</A>
-                          |
-                          +--<B>display.vt320</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>vt320</B><DT>extends <A HREF="../display/Terminal.html">Terminal</A><DT>implements <A HREF="../display/TerminalHost.html">TerminalHost</A></DL>
-
-<P>
-A DEC VT320 Terminal Emulation (includes VT100/220 and ANSI).
-
- The terminal emulation accesses the applet parameters to configure itself.
- The following parameters may be set. Default values are written in
- <I>italics</I> and other possible values are <B>bold</B>.
- <DL>
-  <DT><TT>&lt;PARAM NAME="Fx" VALUE="<I>functionkeytext</I>"&gt</TT>
-  <DD>Sets the string sent when the function key Fx (x between 1 und 20)
-	is pressed.
-  <DT><TT>&lt;PARAM NAME="VTcolumns" VALUE="<I>80</I>"&gt</TT>
-  <DD>Sets the columns of the terminal initially. If the parameter
-      VTresize is set to <B>screen</B> this may change, else it is fixed.
-  <DT><TT>&lt;PARAM NAME="VTrows" VALUE="<I>24</I>"&gt</TT>
-  <DD>Sets the rows of the terminal initially. If the parameter
-      value of VTresize <B>screen</B> this may change!
-  <DT><TT>&lt;PARAM NAME="VTfont" VALUE="<I>Courier</I>"&gt</TT>
-  <DD>Sets the font to be used for the terminal. It is recommended to
-      use <I>Courier</I> or at least a fixed width font.
-  <DT><TT>&lt;PARAM NAME="VTfontsize" VALUE="<I>14</I>"&gt</TT>
-  <DD>Sets the font size for the terminal. If the parameter
-      value of VTresize is set to <B>font</B> this may change!
-  <DT><TT>&lt;PARAM NAME="VTresize" VALUE="<I>font</I>"&gt</TT>
-  <DD>This parameter determines what the terminal should do if the window
-      is resized. The default setting <I><B>font</B></I> will result in
-      resizing the font until is matches the window best. Other possible
-      values are <B>none</B> or <B>screen</B>. <B>none</B> will let nothing
-      happen and <B>screen</B> will let the display try to change the
-      amount of rows and columns to match the window best.
-  <DT><TT>&lt;PARAM NAME="VTscrollbar" VALUE="<I>false</I>"&gt</TT>
-  <DD>Setting this parameter to <B>true</B> will add a scrollbar west to
-      the terminal. Other possible values include <B>left</B> to put the
-      scrollbar on the left side of the terminal and <B>right</B> to put it
-      explicitely to the right side.
-  <DT><TT>&lt;PARAM NAME="VTid" VALUE="<I>vt320</I>"&gt</TT>
-  <DD>This parameter will override the terminal id <I>vt320</I>. It may
-      be used to determine special terminal abilities of VT Terminals.
-  <DT><TT>&lt;PARAM NAME="VTbuffer" VALUE="<I>xx</I>"&gt</TT>
-  <DD>Initially this parameter is the same as the VTrows parameter. It
-      cannot be less than the amount of rows on the display. It determines
-      the available scrollback buffer.
-  <DT><TT>&lt;PARAM NAME="VTcharset" VALUE="<I>none</I>"&gt</TT>
-  <DD>Setting this parameter to <B>ibm</B> will enable mapping of ibm
-      characters (as used in PC BBS systems) to UNICODE characters. Note
-      that those special characters probably won't show on UNIX systems
-      due to lack in X11 UNICODE support.
-  <DT><TT>&lt;PARAM NAME="VTvms" VALUE="<I>false</I>"&gt</TT>
-  <DD>Setting this parameter to <B>true</B> will change the Backspace key
-      into a delete key, cause the numeric keypad keys to emit VT100
-      codes when Ctrl is pressed, and make other VMS-important keyboard
-      definitions.
-  <DT><TT>&lt;PARAM NAME="F<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
-  <DD>Function keys from <I>F1</I> to <I>F20</I> are programmable. You can
-      install any possible string including special characters, such as
-      <TABLE BORDER>
-      <TR><TD><TT>\e</TT></TD><TD>Escape</TD>
-      <TR><TD><TT>\b</TT></TD><TD>Backspace</TD>
-      <TR><TD><TT>\n</TT></TD><TD>Newline</TD>
-      <TR><TD><TT>\r</TT></TD><TD>Return</TD>
-      <TR><TD><TT>\xxxx</TT></TD><TD>Character xxxx (decimal)</TD>
-      </TABLE>
-  <DT><TT>&lt;PARAM NAME="CF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
-  <DD>Function keys (with the Control-key pressed) from <I>CF1</I> to <I>CF20</I> are programmable too.
-  <DT><TT>&lt;PARAM NAME="SF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
-  <DD>Function keys (with the Shift-key pressed) from <I>SF1</I> to <I>SF20</I> are programmable too.
-  <DT><TT>&lt;PARAM NAME="AF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
-  <DD>Function keys (with the Alt-key pressed) from <I>AF1</I> to <I>AF20</I> are programmable too.
- </DL>
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: vt320.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei?ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../serialized-form.html#display.vt320">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="field_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Field Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#INSERT">INSERT</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Insert key.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#KEYCAPS_LOCK">KEYCAPS_LOCK</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#KEYINSERT">KEYINSERT</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#KEYNUM_LOCK">KEYNUM_LOCK</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#KEYPAUSE">KEYPAUSE</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#KEYPRINT_SCREEN">KEYPRINT_SCREEN</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#KEYSCROLL_LOCK">KEYSCROLL_LOCK</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;char[]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#unimap">unimap</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../display/vt320.html#vt320()">vt320</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#addNotify()">addNotify</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initialize terminal.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String[][]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#getParameterInfo()">getParameterInfo</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.awt.Dimension</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#getSize()">getSize</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#getTerminalType()">getTerminalType</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;evt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handle events for the terminal.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;char</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#map_cp850_unicode(char)">map_cp850_unicode</A></B>(char&nbsp;x)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#putChar(char)">putChar</A></B>(char&nbsp;c)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#putChar(char, boolean)">putChar</A></B>(char&nbsp;c,
-        boolean&nbsp;doshowcursor)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#putString(java.lang.String)">putString</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put String at current cursor position.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#send(java.lang.String)">send</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dummy method to handle input events (String).</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../display/vt320.html#toString()">toString</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return the version of the terminal emulation and its display.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-<A NAME="field_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Field Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="KEYPRINT_SCREEN"><!-- --></A><H3>
-KEYPRINT_SCREEN</H3>
-<PRE>
-public static final int <B>KEYPRINT_SCREEN</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="KEYSCROLL_LOCK"><!-- --></A><H3>
-KEYSCROLL_LOCK</H3>
-<PRE>
-public static final int <B>KEYSCROLL_LOCK</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="KEYCAPS_LOCK"><!-- --></A><H3>
-KEYCAPS_LOCK</H3>
-<PRE>
-public static final int <B>KEYCAPS_LOCK</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="KEYNUM_LOCK"><!-- --></A><H3>
-KEYNUM_LOCK</H3>
-<PRE>
-public static final int <B>KEYNUM_LOCK</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="KEYPAUSE"><!-- --></A><H3>
-KEYPAUSE</H3>
-<PRE>
-public static final int <B>KEYPAUSE</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="KEYINSERT"><!-- --></A><H3>
-KEYINSERT</H3>
-<PRE>
-public static final int <B>KEYINSERT</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="INSERT"><!-- --></A><H3>
-INSERT</H3>
-<PRE>
-public static final int <B>INSERT</B></PRE>
-<DL>
-<DD>The Insert key.</DL>
-<HR>
-
-<A NAME="unimap"><!-- --></A><H3>
-unimap</H3>
-<PRE>
-public static final char[] <B>unimap</B></PRE>
-<DL>
-</DL>
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="vt320()"><!-- --></A><H3>
-vt320</H3>
-<PRE>
-public <B>vt320</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="toString()"><!-- --></A><H3>
-toString</H3>
-<PRE>
-public java.lang.String <B>toString</B>()</PRE>
-<DL>
-<DD>Return the version of the terminal emulation and its display.<DD><DL>
-<DT><B>Overrides:</B><DD>toString in class java.awt.Component</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getParameterInfo()"><!-- --></A><H3>
-getParameterInfo</H3>
-<PRE>
-public java.lang.String[][] <B>getParameterInfo</B>()</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD><A HREF="../display/Terminal.html#getParameterInfo()">getParameterInfo</A> in class <A HREF="../display/Terminal.html">Terminal</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="addNotify()"><!-- --></A><H3>
-addNotify</H3>
-<PRE>
-public void <B>addNotify</B>()</PRE>
-<DL>
-<DD>Initialize terminal.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>parent</CODE> - the applet parent where to get parameters from<DT><B>Overrides:</B><DD>addNotify in class java.awt.Panel<DT><B>See Also: </B><DD><A HREF="../display/Terminal.html"><CODE>Terminal</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getSize()"><!-- --></A><H3>
-getSize</H3>
-<PRE>
-public java.awt.Dimension <B>getSize</B>()</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD><A HREF="../display/Terminal.html#getSize()">getSize</A> in class <A HREF="../display/Terminal.html">Terminal</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getTerminalType()"><!-- --></A><H3>
-getTerminalType</H3>
-<PRE>
-public java.lang.String <B>getTerminalType</B>()</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD><A HREF="../display/Terminal.html#getTerminalType()">getTerminalType</A> in class <A HREF="../display/Terminal.html">Terminal</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;evt)</PRE>
-<DL>
-<DD>Handle events for the terminal. Only accept events for the scroll
- bar. Any other events have to be propagated to the parent.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>evt</CODE> - the event<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="send(java.lang.String)"><!-- --></A><H3>
-send</H3>
-<PRE>
-public boolean <B>send</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Dummy method to handle input events (String).
- This is only needed if our parent is not TerminalHost<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../display/TerminalHost.html#send(java.lang.String)">send</A> in interface <A HREF="../display/TerminalHost.html">TerminalHost</A><DT><B>Parameters:</B><DD><CODE>s</CODE> - String to handle<DT><B>Returns:</B><DD>always true<DT><B>See Also: </B><DD><A HREF="../display/TerminalHost.html"><CODE>TerminalHost</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putString(java.lang.String)"><!-- --></A><H3>
-putString</H3>
-<PRE>
-public void <B>putString</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Put String at current cursor position. Moves cursor
- according to the String. Does NOT wrap.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>s</CODE> - the string<DT><B>Overrides:</B><DD><A HREF="../display/Terminal.html#putString(java.lang.String)">putString</A> in class <A HREF="../display/Terminal.html">Terminal</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putChar(char)"><!-- --></A><H3>
-putChar</H3>
-<PRE>
-public void <B>putChar</B>(char&nbsp;c)</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD><A HREF="../display/Terminal.html#putChar(char)">putChar</A> in class <A HREF="../display/Terminal.html">Terminal</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="map_cp850_unicode(char)"><!-- --></A><H3>
-map_cp850_unicode</H3>
-<PRE>
-public char <B>map_cp850_unicode</B>(char&nbsp;x)</PRE>
-<DL>
-<DD><DL>
-</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="putChar(char, boolean)"><!-- --></A><H3>
-putChar</H3>
-<PRE>
-public void <B>putChar</B>(char&nbsp;c,
-                    boolean&nbsp;doshowcursor)</PRE>
-<DL>
-<DD><DL>
-</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/Terminal.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="vt320.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/help-doc.html b/web/root/telnet/Documentation/Source/help-doc.html
deleted file mode 100644
index dfaccc436d..0000000000
--- a/web/root/telnet/Documentation/Source/help-doc.html
+++ /dev/null
@@ -1,146 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:37 CEST 1999 -->
-<TITLE>
-: API Help
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Help</B></FONT>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H1>
-How This API Document Is Organized</H1>
-</CENTER>
-This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.<H3>
-Overview</H3>
-<BLOCKQUOTE>
-
-<P>
-The <A HREF="overview-summary.html">Overview</A> page is the front page of this API document and provides a list of all packages with a summary for each.  This page can also contain an overall description of the set of packages.</BLOCKQUOTE>
-<H3>
-Package</H3>
-<BLOCKQUOTE>
-
-<P>
-Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:<UL>
-<LI>Interfaces (italic)<LI>Classes<LI>Exceptions<LI>Errors</UL>
-</BLOCKQUOTE>
-<H3>
-Class/Interface</H3>
-<BLOCKQUOTE>
-
-<P>
-Each class, interface, inner class and inner interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:<UL>
-<LI>Class inheritance diagram<LI>Direct Subclasses<LI>All Known Subinterfaces<LI>All Known Implementing Classes<LI>Class/interface declaration<LI>Class/interface description
-<P>
-<LI>Inner Class Summary<LI>Field Summary<LI>Constructor Summary<LI>Method Summary
-<P>
-<LI>Field Detail<LI>Constructor Detail<LI>Method Detail</UL>
-Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.</BLOCKQUOTE>
-<H3>
-Tree (Class Hierarchy)</H3>
-<BLOCKQUOTE>
-There is a <A HREF="overview-tree.html">Class Hierarchy</A> page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with <code>java.lang.Object</code>. The interfaces do not inherit from <code>java.lang.Object</code>.<UL>
-<LI>When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.<LI>When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.</UL>
-</BLOCKQUOTE>
-<H3>
-Deprecated API</H3>
-<BLOCKQUOTE>
-The <A HREF="deprecated-list.html">Deprecated API</A> page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.</BLOCKQUOTE>
-<H3>
-Index</H3>
-<BLOCKQUOTE>
-The <A HREF="index-all.html">Index</A> contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.</BLOCKQUOTE>
-<H3>
-Prev/Next</H3>
-These links take you to the next or previous class, interface, package, or related page.<H3>
-Frames/No Frames</H3>
-These links show and hide the HTML frames.  All pages are available with or without frames.
-<P>
-<H3>
-Serialized Form</H3>
-Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
-<P>
-<FONT SIZE="-1">
-<EM>
-This help file applies to API documentation generated using the standard doclet. </EM>
-</FONT>
-<BR>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Help</B></FONT>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/images/blue-ball-small.gif b/web/root/telnet/Documentation/Source/images/blue-ball-small.gif
deleted file mode 100644
index d4c5cde5b005117d443e595db216e8a212831f75..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 255
zcmZ?wbhEHbWMg1s_{zYrfB$|4hW}cM-Hp}9+?~z|3!KYIKF7jvjf3Go2g7@IhGW&$
z$JiOpi3t2RH2E*U@t=d?IS0d>p5_0S9{VpL@SmAsFDt_o5rGyB#sB;q*O(YuSQw(%
z8Sb$$Y-MLi-oN)4C&OMghP4cgQ49=T0s_S>48?2=Ufy1FQj^ayPy;CbWMKrEqXQy9
neqvygacC&uFklhjN_22q8N(ps=f0USLPzzor^l_FW(I2jea<(J

diff --git a/web/root/telnet/Documentation/Source/images/blue-ball.gif b/web/root/telnet/Documentation/Source/images/blue-ball.gif
deleted file mode 100644
index edc29b786ce52736bf9f2a1ac38aaa78b1799e92..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 925
zcmZ?wbhEHb<YC}p_|Cxa|Nnmm28Kh24sGAQJuNNm%a<=R_AlDMfB*C6&-3&185sUA
zTC_-3G<wdQIbmU83=A9W`Kwb>Qlc3cDhjfrg2M~zPiZN3PnvhxP=0fM*#d_D4D;KQ
z82;BY{Qn;q*yA0zaq-6MTMt~^zIAJ%gV~kKS7Q13=FOhtsHSPe%+uH0uqMv@(6Q<>
z=bX-+6F7M;`Pwyx-~Sn&zh^jnjN#a^>f`4a{{I)4Hf`GfKZd{mb3A^|Fntcg|5rW#
zpDg|UUtsTEhW;rGXU?2yY7zMTU-A4kj+PdN@F<2`_ZZf1Wr#~=IC5<7_Pq?t*D^#!
zGI)706c-B=7BjedG0d6cb?#hpb~Zz2kKwWEo^wvw=LEdYC0Ac#aQe>>^q;}&J%iIR
z2FqjBQ_eA1{uhY)Z&>}Gf#E+#&~pZ-ISiWr8UFw8x%PkQ`~Lz#dl@XJFle?282(pG
zzQ*Cy!k`(&AaReuX)A+aGK0jiy?ggE82<myu$I9vib2tfL84f|uo%P;nB$dvF1aO}
zL9xfstFii+yVE&gfpa;@=U5o7aWMSnV0h2YaICue*t>V{^!@!aGcykyIB@y$<^R_i
zzTVq>YpKHjr40Y;1OB@*oQ;WDs-?9cNcXsC>ji}gYya!d_&sCRStFyfMmC!Z^}55A
zx|JAG7~<zGNISdA<KmfJp_gwJTzl8_u1ot7JHxq{9Wz7({u`S77vT8M!SI}eVNTET
z|4Wbkmk{{R%&?c0VTy=Ai-zKVevWHQ3@t1SQS1!&*ci65GbHcddyJD|FB`*J2F54`
z1}_1DVityCHU=+muQ{p7=NN_#Q2fcl$iTqPpaXI;C{HkOR59>#$ari>P;hK!<KzmN
z5TMA)#UtzFl98||gez=g)>E#Ft6i8_H8WVXr>$|?oN-mi;zWRU*tWd0Rju0M-m&XS
zmfmumJpC+lQ`*^2U%S;dBpj-e^>BGDsvC2-LXbNwXTiqA!$qvOvl16vayeA=R4B|x
O({VHV87*E825SIlbVM`&

diff --git a/web/root/telnet/Documentation/Source/images/class-index.gif b/web/root/telnet/Documentation/Source/images/class-index.gif
deleted file mode 100644
index 7f276bcb242e6136502f60bded36f5326bab534a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1497
zcmZ?wbhEHbyuqN#@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6O
z@t<Kmr;Nvj1qYisgtcN$Y*=`>otKfJ!DHj1qumn5S$9qtE<DmHEAGYPxoOGC$%^((
zE}j9Zr>FC{ADZL&QQ5aqFaOn<m7AA4&NB#I_vNRMM&Nw2d@0*s0WU5zdC$Dm^C}?C
zWmS-%)z)9enPL4Aifyc?Lzi4%&$WGC?oP9(*H&fCkILb`y87~r@{7yP?%w_;Zgb(^
zO}sigH18f%_?cC^dV0>ob^Wj9)Ox0VO59PTsvgEO^Yej?j*j*FY@%K`o}T=(%1q{`
z)YE-Erl&>kM6Jm@Gc#2>bnh+GfUOdTqvB+?f6l+Pb^Ro}y?4Ik&D+?k9LFbJQ25L<
ze|p#-8_T%&w-fXKf1ADC{M`0MYrD5geqHzN^PB7QYU>Scla4<=x%m9r*>$xKpI&6W
zTln#0x_IgRMsc|^qo2>C6Lz?ohZk?(TE3uB`RDY>wO6mIEGXm4OTSklR3#yq|EtjT
ze(7A>6Y|;mR%v&<6c@F{H*yGxD|h66>`3hV(3+9dbHq?1x?Vhu?O~HBqeXnb=c*G*
z&0LSJt4#6#CviKd#nAFex7{??glU$$JeCywsh#mC;Lg`;caxn~r7xZC$hI<du2op(
zlAiCGYDzOJ&n*)&Z3z+%U)Z6xN@Jmu=B?#(C*0)Jm|H(jb9rA~+N!WcD|k<3E@#lu
zTD53i*R70&OO<}zS{&kQsW`_zXj|or<<oj^y<D+l(J#SuE9ZH=Ub8Fh#wzbZwT{eX
z*H?JmT(tF>SH!vtZmZv{U&iL9wc()J%!#chJdeByYkQZKz3Rdu(Y4#JF7nn}>{sip
z*M6dFhR)hOU8}WCd!JSdY}xluYSsR}M`_FUUVpag{XvzFzt``6p_g@c*(IT_4@X=y
zeGC?HZR9RIAX0v3<1yakoC{l*R^@y;Igw9glf$J&Z$6#oo67z9jES;J)>(_^F#!v$
PzQ6f=-r>4T0E0CEs5#8p

diff --git a/web/root/telnet/Documentation/Source/images/constrct.gif b/web/root/telnet/Documentation/Source/images/constrct.gif
deleted file mode 100644
index d1a6ae507ca808dd0df7f7cef51fbb3a4aa7050a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1565
zcmZ?wbhEHbe9oZ8@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6O
z@t<Kmr;Nvj1qYisgtcN$Y)EuuV36^e<FRqk(QXOjtUDEgNiKZ~x=uWvo0goM%xPQa
za<XW_sc8nur$T-dJ@cHZo%`yJr;z&jd2;n!vbPL10(<THwPLe2Jlnd&N8GB{YpVLW
z)d5SNZOQtQb#+nv)+E{8UtfA`PM4qN+dc8kjSajT51Gsgdgr)3^*!5JtpcssmYUy>
zczLETxZRt>`rouid_#D9@6}~<Z+$UL-c-nWIj2&>F#1rlu=ly>>+dqAPtj*Ln|rh9
zY5z*cP0KWYnM9pxH@!da$F+}<cV~RPruVjLOWxJP6F;-rZ8HBfb#thGe{WS$&iX4W
zx%2C6cf5abNww<#^mj2IpFeu2xR`(a&Gg?#k1RaAf32-a=G)uX`RihfYrn4jd{KQ`
z&1cK0u?@GEe!t)N?WuZP{fE|L@s&q(f5>M3pE+GT{;;ctRG!?mbn%KGAIopH+$pt)
zYn7^tc;v03l(DFlOUvwG151<I<DRRM)02w6{7jFV@O`gDWN(u042cfCcQ2;bS>KxW
zu-N(8_C+a<$J&!ij?Z;djI#0bj1J-`(@dSJo7JHd`@V7cof+ZNUMM#+v~@fzKI7W?
zWOB}}OsTnUR+TD~K0ZADB&teirSb$$Ee_H47`7izdkfx$KB&~<%6vSpbK8y#SH*di
z856YvlV7b|7T5Y})rv<)Uag7<{1yCawcN>9^OxCP@^~@FUQcuJ`UB6tyjZbt@2ppA
zj&18&vvJieF2T(yj=R@voXu77to1P4X{A|rK5;Uv{Z>2s&6bmL+V6Ipm{gs$<&s_W
zx=qP*cdedu^IGNd?MbuP*Y92<_B(Iwdp4eTJD&;hWN&`)N!wsIyLQg1!W&6H=jN_6
z+mpZZ<EB^AUOh%C8#c?ofBoU81ar^wy-SvD%{%s2XwAFB9(N;WL<w#E^ZtZxyY`)O
z=KX67vyAmsKf5ZL_gp`pAT@o%AxHDGA-g%0-{$Z13(wu^pm;gj@KSjB+fv8X^WJ>8
ko-%#ygcB=ft^IZ@A^7yS+Xbh&zuzgjp1b`P<3k2(0KZ@O?f?J)

diff --git a/web/root/telnet/Documentation/Source/images/constructor-index.gif b/web/root/telnet/Documentation/Source/images/constructor-index.gif
deleted file mode 100644
index 435cac42386bbd6537502c2cf4f0e61dbe5bbf67..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1711
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

diff --git a/web/root/telnet/Documentation/Source/images/constructors.gif b/web/root/telnet/Documentation/Source/images/constructors.gif
deleted file mode 100644
index d1a6ae507ca808dd0df7f7cef51fbb3a4aa7050a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1565
zcmZ?wbhEHbe9oZ8@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6O
z@t<Kmr;Nvj1qYisgtcN$Y)EuuV36^e<FRqk(QXOjtUDEgNiKZ~x=uWvo0goM%xPQa
za<XW_sc8nur$T-dJ@cHZo%`yJr;z&jd2;n!vbPL10(<THwPLe2Jlnd&N8GB{YpVLW
z)d5SNZOQtQb#+nv)+E{8UtfA`PM4qN+dc8kjSajT51Gsgdgr)3^*!5JtpcssmYUy>
zczLETxZRt>`rouid_#D9@6}~<Z+$UL-c-nWIj2&>F#1rlu=ly>>+dqAPtj*Ln|rh9
zY5z*cP0KWYnM9pxH@!da$F+}<cV~RPruVjLOWxJP6F;-rZ8HBfb#thGe{WS$&iX4W
zx%2C6cf5abNww<#^mj2IpFeu2xR`(a&Gg?#k1RaAf32-a=G)uX`RihfYrn4jd{KQ`
z&1cK0u?@GEe!t)N?WuZP{fE|L@s&q(f5>M3pE+GT{;;ctRG!?mbn%KGAIopH+$pt)
zYn7^tc;v03l(DFlOUvwG151<I<DRRM)02w6{7jFV@O`gDWN(u042cfCcQ2;bS>KxW
zu-N(8_C+a<$J&!ij?Z;djI#0bj1J-`(@dSJo7JHd`@V7cof+ZNUMM#+v~@fzKI7W?
zWOB}}OsTnUR+TD~K0ZADB&teirSb$$Ee_H47`7izdkfx$KB&~<%6vSpbK8y#SH*di
z856YvlV7b|7T5Y})rv<)Uag7<{1yCawcN>9^OxCP@^~@FUQcuJ`UB6tyjZbt@2ppA
zj&18&vvJieF2T(yj=R@voXu77to1P4X{A|rK5;Uv{Z>2s&6bmL+V6Ipm{gs$<&s_W
zx=qP*cdedu^IGNd?MbuP*Y92<_B(Iwdp4eTJD&;hWN&`)N!wsIyLQg1!W&6H=jN_6
z+mpZZ<EB^AUOh%C8#c?ofBoU81ar^wy-SvD%{%s2XwAFB9(N;WL<w#E^ZtZxyY`)O
z=KX67vyAmsKf5ZL_gp`pAT@o%AxHDGA-g%0-{$Z13(wu^pm;gj@KSjB+fv8X^WJ>8
ko-%#ygcB=ft^IZ@A^7yS+Xbh&zuzgjp1b`P<3k2(0KZ@O?f?J)

diff --git a/web/root/telnet/Documentation/Source/images/cyan-ball-small.gif b/web/root/telnet/Documentation/Source/images/cyan-ball-small.gif
deleted file mode 100644
index 7f74357443af1e4323a2e6597c59a2e8a508b149..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 255
zcmZ?wbhEHbWMg1s_{zY*;NivK<<1Zl$`BRBkeJL+T)@y$$1r6g!|XW>YgaOC-N>+a
zE5qKs495;JoIA;I?JUE+n+(q%F}#1p@c#?L|DT-KE^+_=C0Lv<c<z``OOw$5uR{NS
zDRp!!{r_$J|BK7H)82Dtq@F&PdhTTHkz=*T4)^|lu<ZZs{rmUtKYEOT8bI+U3nK%A
tI)e_#Fp!@Z*kle&E~w!0)!==&WJ_d<pFqbU0}EL`RtAp>$(&{eYXJIXPeuR$

diff --git a/web/root/telnet/Documentation/Source/images/cyan-ball.gif b/web/root/telnet/Documentation/Source/images/cyan-ball.gif
deleted file mode 100644
index 97ca1f2b6e3956db2a34dea63b25bf308fa0085c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 925
zcmZ?wbhEHb<YC}p_|Cw<;NivK<<1Zl$`BRBkeJL+T)@y$$1r6g!|XW>YgaOC-N>+a
zE5qKs495;JoIA;I?JUE+n+(q%F}#1p@c#?L|DT-KE^+_=C0Lv<c<z``OOw$5uR{NS
zDRp!!{r_$J|BK7H)82Dtq@F&PdhTTHkz=*T4)^|lu<ZZs{rmUtKYENoB#q(524)5g
zhWGCo{--fK-@xFY!|<MgA)A3gnt?fm0Spot7&y2Y7#RLDFg$0NoWQ_P$-pp&!S4$L
zKLdjT0|NsG!x07s24)5)28MdDs^bjN?->|gf;7JuX83=c;YAjM1_Q$#28II+44d9F
zI8-q(1TZkfFffQPFzjPsuwY<#&+z^~!~d5I&wUtnh%n^bV9;h@kYHeNU|_h%$k6hE
zp@ETs;e!Cfdj{wKj0P{97#1WjFjg^G1u*=GVfZgH>BYVSdKL@}FBlj)7#JCx85tb;
z7!FTTsAOO;XJB~1z|g?J$H2hAXu#0G@IRd4e?G(igAD(T7~k7Z_^-qGym`d|KZd1M
zvMEAb44hjS7#QX;9AjYEF`t3q&JTtISqz&PU|#hCdwzi*!!-$zRsKp0Weg0d3=BmK
z4CRarMH~#75)37c45?cf6c`yQ<QOt1GZeEhlt?jTGC1ThFr*)6P>5kjV_?W+V91f@
zOLd*3pvR%^%%CsCkhg^)mEnB}Q<eU=fbu^v<$4U|LJX<v87deU3YZx3A2JjPFqY_X
z=sPo{2r*=?XDF^`$o|h@_@6=lID;<(Lp}pTu>wOPgJ34Z`(%b}eFn)Cf$V&S>|zE9
zeTJNLP*e<7toW0Kk%2*-K?meOP@Z7msABNvknz~?po3q+phhCl;7Bv4u!cv5K#~F{
zyRb>c27!f)OhQcdJ{tlK9cg3d=1URiXl!ETm(|j7NJwDt<`7h{2ymF(a8_7eOebMN
z!?Fej@qjrBg$xH7nOQj)I2e)^bTF~9vD`S7xFASKoQ-D(gOjG?X7w{#yc`VH0PWf2
AdH?_b

diff --git a/web/root/telnet/Documentation/Source/images/error-index.gif b/web/root/telnet/Documentation/Source/images/error-index.gif
deleted file mode 100644
index 22835ff8c64d5af8369f589cf17819263a8e8fbc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1438
zcmZ?wbhEHbT*sis@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6A
z@sD9Wr;Nvj1qYisgtcN$Y*^^bAi!ubN5eqrXt#uM)}0d@lUzDw`RjN*H!V3inZ<q4
zo^OIrPE9vR7R&iLY1x_C7QVM!RvIloIL$%v*O!}{7anc3Zm;5*6_R;&kw?E)j@1;+
zfTcmMyUtimeSK+42s>LT*R@sQJx&LwwQ5aU8MsZSKklkkSjxE#8L`KFqp!U3-ygVl
zmMwS1v&ebv%-(OfD>ghl#QBi#+U;v^qgH0}W*=EyvF`ESY1-%C{`vm->BR-1POYaW
zl%}8W%{6lK{k1Xjwp04yb9<(Ix_vTMSKM!vbz$bcMb6*P-M#$L@7!^9W&b!C<HFY$
z7Q2V<vo=2atSI*1<*9bHKTB?1-zY8rUfSa8(<}4a_2uX6$^U#?bNP3@dA0wpOjk|6
zSH}>PB44}ZVELR}rZ|_-^619pa#a&-ugE(ks_uwyd7axX?IHbWdQ@AfWyQjFjdh|6
zn%Fk6CN`I9PEYEV{bjy5LtD(_apfw{6W2466=%q{sn?yjS8G?~5nuaORdYeXCeCRO
z^1p9ACKYFEmAPojpPv?wYV3<FRq`Y!o|tBGiKi`Xmi*e6&t?}eeNFSuzSQ|_x<lWU
zE4kKTCsig29*kTbns-a{#ljpmtL2M2jc#cy%$U`dK5_5Vn`v{}xK3SJB%t^zbMcHa
zsh3NeXN77kS)8_WNtuXkRmQT~S%<1#E#I<_>%huByIYzoSEgyDuQZ(}v1-Leq1P)n
zmQT7G=D7EqmCmM3Qc^b?cRpLSa?_1lS=yTp+<TQ)b45=}W80ImBf9I3HB~FjdAjZO
z<aODryyxxyqGcVlhgmM-{l18wp4$5v*lW%%XA+NDzno3o=lu>Y^EY{$_}pXi92l$t
D>*l58

diff --git a/web/root/telnet/Documentation/Source/images/exception-index.gif b/web/root/telnet/Documentation/Source/images/exception-index.gif
deleted file mode 100644
index e3830d9c52e7899bb62b56f56f71ba304108ed83..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1707
zcmZ?wbhEHblwnk3_|5<U#taN;3=A_F7|t*-{AVy`U@$gjFivAIp2=W*hQathNHWcs
zAuWv|Z6-t78HTj~Ahk1%8D^$2%$&(E^9;kx{~)brj2X_PF`SvnaOModng0y`85sT>
zGyG3u_&<~3{~3n=|3L<Wj7~FVm}$&##+czh$R1;3W8*Yq<C(_BXN-;igRD(6Hcm@3
zPMc|*cE&jEKgbm`jg4of8PA+)JoAk4%>N)qoiR2(lV*Hortz6G#%KN;|7S4%Z*2TO
z&G`RJ<Ns%j|Nl<|dC52p<mj1cAZPst`2b{7S{lf#Gik>EK^{soP6L6NX=!KD(*A?|
zH`6$6W?I_JnQ1f6q|N*f^6nX9kmQ+}X=l!)o%x^kpCRqPaoYd1wEr{H{+~(v|9>VZ
z?2KoELTBbokpKUK!Up7}w3#4BpP6a=9~4v|i_>O;j6O3n?LR2wK+<V5LEy~Hng2ln
zdBzxI?U|V~&zzZg=Ksw93^V^5&-|Y@^Z(45|If_)|Njgql8n!QqG9G4P(c3&g*qtg
z(m+~4!SNpy32DY>Kwg@82IT1fpg5Un4D!LunP)&o{|80U8Do%#K>Raj&ip^~pW)1Z
z<1_!$&itQw=Kq;9|Nny`5FGs=SAb&_6rbQo0yzpC?x5%e2PMdj;LriZHrT@;CxiU}
ziu^OiprAT46J#*h=>PvgvGo5x$WLIa7<hSkrKF@ZG&D?2O`V;c0|EkKV`H<kvnwhp
z+S=MCPoBJR;lg$6*6rTC`{c=!*REZA^5n_Kk01a4|IbbVQ2fcl$iQ%&K?jr*KzV|l
z<3GcCP8p943l27O2y4Zh*s$<$I}4*ni^s-AN4q7Av+kVOxY)f{NZpL*=f))t6V-xO
z#hl!fdSn8F+o6)qLbbEAEs9^&bQUc<JD0D1oeJlc6@mRuqKA4aLozQ-@t^HAch-az
zXXb@e8l~<uS$%!IdZ*sqECKB+-LcVbbFH@NMy$wvJn3$x=%ZWxnFmkp{k=`$+D_*0
zyS{So(7B&oDk$%^<Hv%Vt2*~`)t%Y4A?)Zx*55rc6$Z&ul6CL-&M7y3zNYtT)U{nw
z#W9DL`bOXTUHRyB*W#nc%4Bw}f4yvX<obDMW*2?DxutuzAMfm<@bkx;+ga<PwmodW
z&}w}>XGiJxLzRpD{(p<BwmA9bZ1wW}aobcrKl_l%UjMJ+L-4cg$wg{<zrIwQ*uM1o
z<+@`J!{*jZw=9iIGwz%|t(o<hdSXP{-}D8Ae_XfUDR(lIP;`E*`)zUu@1$k3+r#cI
zm#_FYGvZ-0yH<xxxlG=UxLEBvjrcYOBURbf1v@+D_3p2o{-9;<-XG#UhD<k-JHOr(
zk*@Wg^fj(hY!=&+N{cYdlnMd0nU8ylS8AkAerlU3-RQ`*GqooC)Q*WUMek<ZpPBQ^
zBEGw!SMySTRMO6=Gjpp}&MUKubBzmVm*RXhAzw)I!CZ-|NaYC`q3p{hh5gcuZ75!}
zGNYx}^wo-$r$4%8E_dRSQeP(3_5WqYst&P^tYw{jS$7v#Oxu}|xBXDnoEf!cGqcP6
zg{)sMT#(c`eZ}H!VOd=oY_nIDAJ}zj<+kH;JLjztGRxA<K3}#vZD}r__RFp8eXeJB
ze>nDA&*#&r-Qjz$T;N{6?@G)K<((S@traGxIPK0}F(GN$8`F>Xvh$;V*vSMP5n^`u
zxJTgn>HK}t@oPRF`Js2!VD_7~)tQINB<JKzWG$2lKcOFfr|^jJWu1w~^{)49Jo>w-
z$M}p5vuw#Z=l64Dc2xh{rLo@o^oiVyZrwSXuNXPoZoMj={CDHE$oqdbpB4F?^W~~!
z|DBvAI*)ICadgRlyY*&B``@yoA~UnET(5}t-FDRYbcgDequi_aUO1n0ShxI$_`2Fj
z`EG@=7S~b_^L{;6+P>oRzM}nIb5He@=LwuEX7)9EbTKlnVy~a}yiIrKZTAbj5j+20
z{>x?Tcjce=Y<l<Wo!m~@UvFftt+Psud3f&k#{%D$Uk;V~yK8QpclQs^uJqPl^Yw;*
dY2mjwh425~{ZOa>@5htr`Tu^3ur6V+1^^!ZLxcbT

diff --git a/web/root/telnet/Documentation/Source/images/field_ix.gif b/web/root/telnet/Documentation/Source/images/field_ix.gif
deleted file mode 100644
index 304ce7d59a25edb878a96399ebcdd77cd9d3a0c8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1443
zcmZ?wbhEHbT*jcr@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6A
z@t<Kmr;Nvj1qYisgtcN$6eu*b^D2AI@z}WNXt#u@9g9L?^09se=PnbDjf;<WNL%*B
zoZPhZ*d(6dSvEH(E%Tmcrh6)9$K>>LQ|<Hhd^jg7T<o!p7n7YOk`b_wuhT0u^NXg}
zYS!6WOQVW4udE5~Z~AH#_}X!!-N`nS-(RC5wuJ5sOYs(Z9kJ8rqtX`bkgVG)s$PHU
zU2U#;XK(XvH8D@|504HrmH+a!`1w9|xxci(u5?Jz)7?4D#(6f=K0QAXakxlLyZCeD
z)dl&J_UT9zW!wmltUls<<J*(@-GQrXdG~z1bo-dAu>PO0@{`Lpbo0mCP5$}$@!bA%
zeY^kU<~}Ui8vkE^*WJSRw<>HWU;p*Hux#UnOew#0MgK~^+~cgbKXdP2){$4O>hpaW
zUp)^iIQwzBaP3Ra<Dr%8R^|_?M1n*YG{`t|#1(J+dToCAx4Y>H?Sgh54?6ecb}Yz_
zekdMMbE7juu1a}PYI2vH*7w-b1-$IaorZhaFZVhNan5WPaa{4BMY-wu4d*KXlc$x6
z7JXNlRC&_l_7v-BH&R*xgN#FGq)fAXJWVyKLa5uNZ28j3Hq+RaPf?xZIb}xjw3X>o
zG~ZUH&!}~KsoEWPXy)R+jI>CB`S~|@PMpW-v{QXae~^~u;;Biq<mY$`t;%ppWs}O9
z*B8f?5mCWp@N&h>zEi7Kt@mmW&0a1rb@|m*^-;H8t=#ac>gBrCk0jTu+m~eZdfimn
z%Ih1JetWfi<(a;$fEm*`pJ*)EXSI6eww14DXm4H=6#aGwk1v<*`a4-#FSgu&=lO1T
zylr>({O-KhdV796%gzg0(>443euiN43#%B#a|%K(->d#`i0Aj|4~GTZbv_;u3I7xB
Hz+epk2%M&7

diff --git a/web/root/telnet/Documentation/Source/images/fields.gif b/web/root/telnet/Documentation/Source/images/fields.gif
deleted file mode 100644
index 6e2a42d940e367265e076bba834884a633461d3c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1241
zcmZ?wbhEHb%wbSt_|5<U#taN;3=A_F7|t*-{AVy`U@$gjFivAIp2=W*hQathNHWcs
zAuWv|Z6-t78HTj~Ahk1%8D^$2%$&(E^9;kx{~)brj2X_PF`SvnaOModng0y`85sT>
zGyG3u_&<~3{~3n=|3L<Wj7~FVm}$&##+czh$R1;3W8*Yq<C(_BXN-;igRD(6Hcm@3
zPMc|*cE&jEKgbm`jg4of8PA+)JoAk4%>N)qoiR2(lV*Hortz6G#%KN;|7S4%Z*2TO
z&G`RJ<Ns%j|Nl<|dC52p<mj1cAZPst`2b{7S{lf#Gik>EK^{soP6L6NX=!KD(*A?|
zH`6$6W?I_JnQ1f6q|N*f^6nX9kmQ+}X=l!)o%x^kpCRqPaoYd1wEr{H{+~(v|9>VZ
z?2KoELTBbokpKUK!Up7}w3#4BpP6a=9~4v|i_>O;j6O3n?LR2wK+<V5LEy~Hng2ln
zdBzxI?U|V~&zzZg=Ksw93^V^5&-|Y@^Z(45|If_)|Njgql8n!QqG9G4P(c3&g*qtg
z(m+~4!SNpy32DY>Kwg@82IT1fpg5Un4D!LunP)&o{|80U8Do%#K>Raj&ip^~pW)1Z
z<1_!$&itQw=Kq;9|Nny`5FGs=SAb&_6rbQo0yzpC?x5%e2PMdj;LriZHrT@;CxiU}
ziu^OiprAT46J#*h=>PvgvGo5x$WLIa7<hSkrKF@ZG&D?2O`V;c0|EkKV`H<kvnwhp
z+S=MCPoBJR;lg$6*6rTC`{c=!*REZA^5n_Kk01Y21{8m?FfuS)XV3xV1W=w};P}t5
zo>RtS!-9j&9Ku>LCpIiR+%6!@G(#d#si8~4SZvOTkB<)a$(eFJ>6p0aM5{vZtUDf`
zo^0(?4c}$5!szMoY3hk?S1c!|`p&US|5RhK<+;Obw@#+g9VVJ5mI!p4P05;~71C|A
z+-z-@=)<rk|GjmgH?F4MoE>>kX{lvcPSo<;n{{hD%~TF=4R=5GH8WiI<l6MFkM>%b
z=-l7u9&N_^C+KPHVV`=ht*eTQVov70bepzE;zRuDT*K2(_ssnA@M1Hovfm$(l86%#
ziRtUKqf65!uWP&}xA*ercgOM@AM0@4+<5K5_CRHEzuWqc9__ExuFc+29QEQ_{Cc(B
z)AM7m)vgrUCcb{>_oA0Y2N#F)%k6x5iEHJ_`S%O=)n-k4dz<gpzdavboRciMU#q~Y
z5%0@(&0U~Q=Hm28HRpKQ6)M=mPCRfCi@TA~Bz-IVzW=YkA`g;f(_SdFNlvp^(8XTV
zmehT0qj-Fy<g?P=PN}*dN<9n`Kkk<?&E4_1#_X88La%t$ibWlqvNx4mT-A;)>@JLy
ycskYm8HdtD-(MZ7(|+=1sF=o2yP4XWw(O?x^sH?!pUo~frulqM$t?*125SJbRD16L

diff --git a/web/root/telnet/Documentation/Source/images/green-ball-small.gif b/web/root/telnet/Documentation/Source/images/green-ball-small.gif
deleted file mode 100644
index 17fea5b32bb808c030865d16d4640a1598e6b76d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 102
zcmZ?wbhEHbWMg1s_{_j?=+L44`}Z^aXPEJOhS6Cgz3y<OZY72khL{~Q7_b4wpDc_F
l42%pqAW4uJ3@pM1CnP5bxO6r8?l@M+;#MIN)ZxZp4FGsI7*PNK

diff --git a/web/root/telnet/Documentation/Source/images/green-ball.gif b/web/root/telnet/Documentation/Source/images/green-ball.gif
deleted file mode 100644
index 71e1b2ec2db92415bf28bc917b772ab874000d31..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 886
zcmZ?wbhEHb<YC}p_|Cxa|Nnmm28Kh24sGAQJuNNm%a<?v_wRrH{CR$UJ_EylS<&b@
zbLNDFg)uN}u;;H%NlA%jV5lg_jtUOfQtZwzTfp$2Vf)st^JdTKYi>AntoqD3r*r27
zPM%A?c8%fpe}?Dp84e#~ICiZ1_&J9E{{{a4G5r0X<MDHb>2nzVzv}t_Wa;<+0(<u|
z^iN@EY7zMTU-A4kj+PdN@F<2`_ZZf1Wr#~=IC5<7_Pq?t*D^#!GI)706c-B=7Bjed
zG0d6cb?#hpb~Zz2kKwWEo^wvw=LEdYC0Ac#aQe>>^q;}&J%iIR2FqjBQ_eA1{uhY)
zZ&>}GBj`DU(;NoP{|x{C_gwqG^!<N<puG&1Qy4T`1PuQxCST)lYGKffVvxAU;Ix&&
zFquK(*xtQ+84UmbXIRT%7{#FI#UN2EU|0-d2+Z+HK9}5*&7jz0=+#(#%-!jnu)w*T
z<Z~<x*Eksdb1=MTXE;_}eJnFG^YZ1(|F1LLTB>k1CT5e8(s9w&3knk$e$ATkd&aD@
zMn-3i^t!{9x|JAG7<PqTzEyDTUDG>uhI41moJl*oDrUzF5rO}PCjSLE{&O%q=U|xA
zv;6<kWB(-t{xdV|Wo4KmBG96t_@AHS8WTec3quq;!#y^Jt?UfR`}ZE>WZ28bu$F-_
zih;pPK%kg~p_q-q%iC*CYVtXTQ2+}8#h)yU3=C`xIv@d1o?zezX5i(J@z}6H$$^7W
z(<owLqH`-Vzg>++#zIC1-oHN<YIrCd<KkrGlrlN7*m<I|XO)kJ##04HCLWEfmy=Q!
dbf^^b?b0-O<};~@>8Ogul>--MFfuY&0|0sW{$>CG

diff --git a/web/root/telnet/Documentation/Source/images/interface-index.gif b/web/root/telnet/Documentation/Source/images/interface-index.gif
deleted file mode 100644
index bf93dda9e3536f9bd1129ff3008d6ca8e998dceb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1648
zcmZ?wbhEHbWMouh_|5<U#taN;3=A_F7|t*-{AVy`U@$gjFivAIp2=W*hQathNHWcs
zAuWv|Z6-t78HTj~Ahk1%8D^$2%$&(E^9;kx{~)brj2X_PF`SvnaOModng0y`85sT>
zGyG3u_&<~3{~3n=|3L<Wj7~FVm}$&##+czh$R1;3W8*Yq<C(_BXN-;igRD(6Hcm@3
zPMc|*cE&jEKgbm`jg4of8PA+)JoAk4%>N)qoiR2(lV*Hortz6G#%KN;|7S4%Z*2TO
z&G`RJ<Ns%j|Nl<|dC52p<mj1cAZPst`2b{7S{lf#Gik>EK^{soP6L6NX=!KD(*A?|
zH`6$6W?I_JnQ1f6q|N*f^6nX9kmQ+}X=l!)o%x^kpCRqPaoYd1wEr{H{+~(v|9>VZ
z?2KoELTBbokpKUK!Up7}w3#4BpP6a=9~4v|i_>O;j6O3n?LR2wK+<V5LEy~Hng2ln
zdBzxI?U|V~&zzZg=Ksw93^V^5&-|Y@^Z(45|If_)|Njgql8n!QqG9G4P(c3&g*qtg
z(m+~4!SNpy32DY>Kwg@82IT1fpg5Un4D!LunP)&o{|80U8Do%#K>Raj&ip^~pW)1Z
z<1_!$&itQw=Kq;9|Nny`5FGs=SAb&_6rbQo0yzpC?x5%e2PMdj;LriZHrT@;CxiU}
ziu^OiprAT46J#*h=>PvgvGo5x$WLIa7<hSkrKF@ZG&D?2O`V;c0|EkKV`H<kvnwhp
z+S=MCPoBJR;lg$6*6rTC`{c=!*REZA^5n_Kk01a4|9_nVp!k!8k%8elgAOPsfbs-8
z$A5<PoH8C8794Em5Y~!0vEiW;g8-w&9FL8Qj&@5JXWcomG5KhRf^F9xiNYl(Cu;;3
zu}B6j^={IQb*k|!espFQXXLRXnUhq{PO@n}#G)0F;4$CT^<9XkNv7`-&uG2UE<vr!
z%R*9<c)Lon!@7e5tNz|H*74h%;<@N-<>rUCwrG1tz16t5;M{iR+jcV16V^xX=lJ;S
zs(09i!@G+$nR+`dHpcF;5_Ma5N__R>eb;&Bv+mjc@yYR0W@WiImR}BDT=ag^-a9it
zT(~elIJ?eDx+HV`blb^&duM&mzV8;=yjCZC$D6I|wyI6Pv#sRyh84O0ZFkuie|>V@
zSKcjJ=Eu7`!5g#xm%NdB<#V_<_xIDBTEo0!H|~bNzia*X<(uPQ^VaT-F8lD|&DQdJ
zb!#|JTu4bSb$!sl_;1<V=3`$~uU2o={qdmeV=4QCn)`p3-)ofN;*f6rB{=PFt3p`$
z?N+X|6A$tYR&Bl0`INKlQTv&*;R{<eoFX0+e!ZG1R%3c^i||CnU(4f4{w_W7q~3PY
zwOK8Ce$VGlwmfw_sngYK%8eG!d+v`)lkB!VX$y3kximAL?Z=X&BaJ&%r{B7$@vJ}c
zUZ?!j9J!y%X3bsu^1<A&UFI=!&PGZs?&oy-adC!Q*-zQI9Z8<|m$XD>Jy@)z)Va8$
z=3m9qWed4hYD_-lDfMEhl9txel7?IVkHiLNB%N9@In!_FlhsR|+*fzbQCz-seP>vw
z`kJbJVQN!i-Lx{?dWBlmvghe}37FnUk`CN<r)akl!`-ackvp!Kxan>Cx^Y)#Pv5Vb
zix+R&b>sOQ)_FH{J)T$X&fWL)-n8{QpD>-t-|=H!biuaU^KPbX)c9vPchW|ujtToZ
z?(Wv#FP?m6(hh;Y)zgnl^XC*C6D#jAI4N_RUAbUaq1XF8RbDDZ`xU3_d^*UyILGLi
zarf=wa}WAtzMOOx)-gExp=njgo-fBDKAkha{>ONi+@aEoS8NyiY&jVvzxV5&#IUo^
zO+~n4O|IPx65DpwW})ncD<1QGi!X%T>-=)!=cQ@dw`-(*k8C~`aQtp=7OP<H=M%N+
z>vp(RG}o9dv{$dW^=L}C-lf};-9JAkOe=r)(?41A-23NCru*G?oIQKpuUC#w&;EM7
V;rf)aH(Rdf{eHWnT5bY^H2`&gTj2lz

diff --git a/web/root/telnet/Documentation/Source/images/magenta-ball-small.gif b/web/root/telnet/Documentation/Source/images/magenta-ball-small.gif
deleted file mode 100644
index bd0584b3c6751baf523dbb07d856bd5443a23a20..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 104
zcmZ?wbhEHbWMg1s_{_j?=+L44`}ZHKp7NjJ|No`$qZky61q|nSCHEM59T#oApfF)q
z=;d1l*WNX~V*mripDc_F42%pqARfpJ1{P6+6OuP1Jp0-L1H76f94AQ}RqV}TVXy`O
DH}oT{

diff --git a/web/root/telnet/Documentation/Source/images/magenta-ball.gif b/web/root/telnet/Documentation/Source/images/magenta-ball.gif
deleted file mode 100644
index 5da03b84d2bf772dd396adf1be8a53e7c46ec54b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 896
zcmZ?wbhEHb<YC}p_|Cxa|Nnmm28Kh24sGAQJuNNm%a<?v_wRrH{CR$UJ_EylS<&b@
zbLNDFg)uN}u;;H%NlA%jV5lg_jtUOfQtZwzTfp$2Vf)st^JdTKYi>AntoqD3r*r27
zPM%A?c8%fpe}?Dp84e#~ICiZ1_&J9E{{{a4G5r0X<MDHb>2nzVzv}t_Wa;<+0(<u|
z^iN@EY7zMTU-A4kj+PdN@F<2`_ZZf1Wr#~=IC5<7_Pq?t*D^#!GI)706c-B=7Bjed
zG0d6cb?#hpb~Zz2kKwWEo^wvw=LEdYC0Ac#aQe>>^q;}&J%iIR2FqjBQ_eA1{uhY)
zZ&>}GBj`DU(;NoP{|x{C_gwqG^!<N<puG&1Qy4T`1PuQxCST)lYGKffVvxAU;Ix&&
zFquK(*xtQ+84UmbXIRT%7{#FI#UN2EU|0-d2+Z+HK9}5*&7jz0=+#(#%-!jnu)w*T
z<Z~<x*Eksdb1=MTXE;_}eJnFG^YZ1(|F1LLTB>k1CT5e8(s9w&3knk$e$ATkd&aD@
zMn-3i^t!{9x|JAG7<PqTzEyDTUDG>uhI41moJl*oDrUzF5rO}PCjSLE{&O%q=U|xA
zv;6<kWB(-t{xdV|Wo4KmBG96t_@AHS8WTec3quq;!#y^Jt?UfR`}ZE>WZ28bu$F-_
zih;pPK%kg~p_q-q%iC*CYVtXTQ2+}8#h)yU3=C`xIv@d1o?zgJW#HwI@z}6H$f@Rs
zKv2*If%zH&i{EIPTu3~kQS;45#DineLQM(Nb%H+(l8)JCpPJw^BOpa`DjP@UoS8*u
oq!`(_F4kD4@yzPdKmFy4r9k?^DXG^M9Q2&9l5v>@GZTX~0LXv`Z~y=R

diff --git a/web/root/telnet/Documentation/Source/images/method-index.gif b/web/root/telnet/Documentation/Source/images/method-index.gif
deleted file mode 100644
index a05e7051160fc7b579abde9ea02e0af9d796cb8e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1588
zcmZ?wbhEHbJkOxU@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3y%53@H9&VPs&q&Y%Oz37|Z|&hejN
zJ*SMvh6M+kIfS)hP829KwDT%!u_$a@bhKMS-|5VWzz4_L`0dN)NCYiDF;OE}$Y$n*
z)Z>#B5?9$sOj>kwmNECPGdGKtJI+^YTqn}CX_;@QTcwrCsxPyACb_#m>N)x4>E({#
z*>SnQinY(K;+|}D=2WOo&?d`^MOR-=)s0wTc9t(ya%)EPj_mjAN>^P^j$h?=by2AF
z6oWgv5@$-?)BgTCzNJx_zs{=i<D~<w+EM%VY)`*`EJD@$UDcM48Ruu@S1skK(!9Uj
zI6A)0=a)&={uEc`W3rK7vu3Q!-v5p#(ky>rQL}p6jq0+`JGY(Rbwu*6W#TP~sk>wU
z)Rbl1J-0Xizx})Fs*~@oX(wM_BU^R*)$Gga`@QB@)jZtl`n<k($My%?Uz~hCqwM>~
z^x0JxT94n$pOLFNt?Jv{kOzg<q3H^FoJ?Y}O?xzVJjgricq6e%u*)Q|LH6JFz`U<t
zuP^Y?>T<u=t}yHBBX^y395OvOTq9y)%;R1xELwR};z@^7-H*q8YH3rIUESuDFPdWd
zDnh(MGVg~%wq8`^titV2zdxO7GmTTVDLAO}e!;tyE8=GxZPL6qdBaw*)DZtu?$cYn
znLN`b*T=c5Hd_>RJ`d^GW-hj{-R#wi8C_Bw(<TcQ-F%ieY2WnaJ%K?H%NI15aV=jy
z!%J)Kio#QxDs#*4TFOm2{PF+Jn5hlBQe&&?zW$uMc;%`rjdkpS$~nG=_@sk29`kC`
zG2Lam<;~`!RWlUVbnfDMy|y9s<byeDoVwQp-k00GX4B(W&))7l(-pmL^Ft<q+%<Ot
zccm}6yo_U2dE?g8db^Hn<IYX_-!yyO?iXx(*6-tZy?W{Nq`GGd))=VH&OgK&UbAkO
zSo#_LgXiUJ3XVV1dXu(Z#eMs_6Efo6A9o&Ex@6-)w#~aYWxTetN|-M<-=}b=?)N#D
zPiy(_*?gpN(X`Fy-nTvdd`Y)@chLn+^_sN~0rr32Rr#c^Ep_yZ-?P+1A^FeaYbn!h
zCtZ#X>fN?EZTs7@^J$?xw{MqRuf6ObdVB5nd)bwOr@!BC_&lZLL5q0akB1%GWds<k
E0iKpTd;kCd

diff --git a/web/root/telnet/Documentation/Source/images/methods.gif b/web/root/telnet/Documentation/Source/images/methods.gif
deleted file mode 100644
index 949e01b8a33818bf52b2d124a504967d0d75e954..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1403
zcmZ?wbhEHboX()e@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6O
z@t<Kmr;Nvj2M!Eu!dfvW3KSVyc)9I*JT@*m+Rdk)bmzv0g~z(&6pLg$FD-JPpychv
zllf`Msc!lBG@p|O%g$sgW?n0)G*&u4k0tWgmY+uH-isWB|M_$kuXI|j)w;;#)E9*-
z^McY}>1difzqZU}<*7YYmowZqN$g$qca~^Q=!W>y+e|a2J-M~ptNxmASJ|68Q)`=_
z9ku@Q_}+qc>qE8L7ha#+-*DK<j;qip_H?=+Ti+}R!<5w%?Cot=mKi*|xcuibHr=YV
znKQ~&H@`VEt1M;X^vvCEwij3CUfyjht>!(WJhSU~e*3())x~e099sPR+727DcgI$X
z|9{I>ns?{Yz3FZL%Xi-0mGNfQ#G-KjDjVNNZg-#8^~R-tc(dm2JvaV6o3C7*d{X^x
z!_$u<GWmZv)q{%JpRtFRm-d>+rfg;W9uvdYwS0Qcwxi!}6vzZwJV^TcwQO$ludCCA
znrlVVWx5RBi6)lvW<8B;HMllCx%aXz$HQu=rjBW)GdXuAPp~VJn9*ae#c`w9^^=5h
zrB2t38*x`sRz97kSQxpW%OQw;LH3WWM`NaPJ=>BxEo<G&Wz%90y}UU4%8_MHTmOl=
zC-<{5ZFm-&f6R0F?Bc4Q0&^RLyp|Lh2Yr2<SS0srd3cAI)r+Mw{Ipgq>Xdr*Vrsao
z_==^o;=EqXRY`o6zASfM)bo{kuOc$;teWcO^*pjCcS`2kXPPTT*Y0_yrQ^PM*7meM
zCRZ+<RVVmf=S0nw%MNv%llJq?)*EWpFPx^Py?(p>R+RVKZ4ahpzjJspZ}qyJm)5=3
T+kIt^_WM0wu0@ABFjxZsTq~VN

diff --git a/web/root/telnet/Documentation/Source/images/package-index.gif b/web/root/telnet/Documentation/Source/images/package-index.gif
deleted file mode 100644
index f894d4210d743ad9a4f0736159ef226279508dc1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1607
zcmZ?wbhEHbe8-^5@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6O
z@t<Kmr;Nvj1qYisgtcNi1Q;0F1eCqzoVcKPv|GYB>&}Udiw?KTI(Mo3C|u$+K`nUI
zlOGo!otiEbeMm;5c-h(6D&CLo%oKilW|mF!G8U~XY8R*L){6D640&;JsYk5TRnF4H
zOG~`xhncRtntZt}c6FSrc4>M<zt-t28}6`Nr){n$Pw{$9c@@1oZ11DJJGQ4gu2El~
zm&+ZI@Ni`itMI$u-wXVAG~WGX`ew(*M+e!Cr=`h!{Ny+@#r}Nk9rG_yCr!L(iLIUa
zHGH1`;YG`0EWc)4pQrnLUz_Nr*C+NwKVG-@_v@UVsLAEAYpcH}KRnwk9ey_QeeBK^
z8}q-({I+m?nziuu|1!P3-zr|+bbKEgZ~H0x*7n8Lef3w&kGy+0b9()bJAW!C9J#th
zuIk>-^0{TVBSU63gor-7U(S5%YIL1dV|YSk|6jKFV*V&q=>)NP>hf_8H`DLtDtJXC
zRi(#nThQfu^tgC>*WMWtb$_mgFYJ7$xI?~1<E;7J=Jtm(6x-h$n$B(({H3y_PWsag
z<r2+9GoBQiOzOBlF(R%qej1ONX43Q?$DM&0envMJ<iFvqd_37vYNujR!am8xQ@u`}
zPz;w}m$7tG@vF9{WyxH>9`~gF@=Tmn+G#E`|M<%t&srPvIG0S*vov2hy+!uK(~9ZO
zPCjXzQYV?YxPIBQ%mv%tstcQLIP||umSN*Huilt#&sMEo^EbCjz2T9jm)7#Rq3v0#
zB!rHvcHX*e%Ih^}Zdtuvm-03%ce8xt>zp;Wy;d(+oanVoeQs%)<;~FZX|Hp)+*wq;
zde@CG@3p(H$K8IrYs)t7{5?C4c}Y)L;yH6c<GNi--bd_uC!N3R!@b=b4)a{sHr)Po
z)9-ah|0ta?*xRO7y=>!!XR|jRx^(Vzj$7W=^h-z7X0OpZDX#vs=$QO_(@n>uyXUN4
zd4%tbR+{9-8iQlb&ttS(f_A>qIkK%xWkbn)Cfmsu_GFzlF_k;iv;N%sU(#1I3#VzB
z`klRXy5w4nx6L<~+;wlaIXN${y|8QdzF6bRvgf^_mzg<L=9YW({nNF`FIa86!$VeX
c-42IqdhdQbcB<t4`D99X-p{A~>{A%50Z8Q!bN~PV

diff --git a/web/root/telnet/Documentation/Source/images/red-ball-small.gif b/web/root/telnet/Documentation/Source/images/red-ball-small.gif
deleted file mode 100644
index f6b3c372ca1e7e70f805981b93c6cd678570e993..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 255
zcmZ?wbhEHbWMg1s_{zYrfB*ht)jj8&vd;;4olCC1#^CgyA?QDY*Lw!1V+@wZs;8V|
zu>3C&_201iKS$7W2B$d;n*V#Q{a^b2zd+Dl2Foc7nk@o`{}q$3aX7UwXhtze++%Rs
z%3zqxAaQK(-n|TlYZ(lq7!<u2B#H$Li$M&5IbO-<l3TJF6nhN47$^pcKUo+-hUkC@
oke?XXWE>g_I20uKI2S4!EsS88&8xncF+^+0Bu|S=In4~#0Kfh|*#H0l

diff --git a/web/root/telnet/Documentation/Source/images/red-ball.gif b/web/root/telnet/Documentation/Source/images/red-ball.gif
deleted file mode 100644
index dca92960148cb3751f9b2de8cf96f26293c15888..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 527
zcmZ?wbhEHb<YC}p_{PBS|Nnmm28Kh24sGAQJuNNm%a<?v_wRrH{CR$UzN~2UoH=vC
z!onCBHrVr5r=+ADI#zw=oYT2;0w>QUU%STe`#;0;_Y8-RF&sNqef%85|NjF2{}}%M
z&++&<!}K`}|6ld|f3o!Ze}TPw8TzL%G_?r){;zoc8b?bDLwFR!t$PgXw=%>fGaNa#
zcl%z3<!c$DA{o5A7>bJp3X2(By%^@q@j7=dIXj!7v&ZmQb<a7c>~jKM=aQ?hF*yBa
z2>Q?9^`61$7=z`p>M7?KEdL8c{Wq-s&k^*T!D$YI=Kr2+|Chf1FA%hs!Ey?NW{ZH~
zf5qf$98N6^no$fA_ZXbEG8iT^NF3X{cQ1qC|NjhY84RNs6ulTEiUkadK@5R8UdiW@
zTe2Axdkno8XbBX5vM@3*urcU>ECIy{1A9&bTT^pOtGR`xrG<H`m9>qHt)0D9vxB3J
zle3Gfy_-Xmg^i25ho`ldw@;I$t&6j-pO>3|z?wkUK*yjh!Q0p@yu3ogw?srng|Inz
myLtOZ2ggLlHpj)s2SnaZXg=i-6%Y^=d#dFb+p|-R4Aubqbg^Cl

diff --git a/web/root/telnet/Documentation/Source/images/variable-index.gif b/web/root/telnet/Documentation/Source/images/variable-index.gif
deleted file mode 100644
index 65cc029e722f8cc8d4663328fe955faab8455982..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1576
zcmZ?wbhEHbJkOxU@SOnyj2Rfx7#L<UFq~mv_|IU>z+i05V4TKaJd?rr41@81kYt)M
zLs}X`+DwMDGYo0}L273jGt5k5m^qVS<{5^W|3O;M7&DwnV>mOD;mjF^GyfU>Gcf!&
zX851R@P8)5|1%8#|APz$8J%X#Fw>afj4{K1kUhr6#>Q#J#xsqL&lns32U(kDY@C*6
zoHo-q?Tm5Se~>F?8XM0{GoCrqc;*@7ng2nKI%8~nCe8TFOye_WjL-Zx{?B0i-`Mzn
zn(_ac#{bV4|Nox`@{(~H$k8*?K+gIP@&U-Ev^0=eXVQ%SgFKXGoCX3j)6&kQrTqu_
zZ>DkD%(S$bGt*|CNt^i}<lQsIAjvZ`)6SepJM%y7KSSDo<Fx;2Y5!-Y{XdiT|Nl%-
z*cs0Rh0e^GApiddg$>9{X){5NJ~Pw!KPad`7N^Yw8GUAE+J8{Ufuz%Bg20)XGyj7E
z@{BRa+A}j}o;fq~%>SAH8D{=Bp7}p*=Kq;9|DT!p|Nj|KBpIIpMZ?T9pn(1l3UyG}
zrGd19g5y6Z64H#%fV?#G49L;{L2)wE803SQGtYpG{tt?xGsYkff%s?6ocVv|Kf{^-
z#%KPgo%uiW%>OfI{{II>AUOI#t^mg<C_cfF1acHO+(FR`4oZ+4!Jz|+ZLo(yP6qn{
z6!~Y2K|yt9Cdgp0(f|L0V(I^Xke|R-G4S&8N=Zp+XlR(4nmRi>2LuGf#>Qr6XIE5I
zw6(QOo;-Qs!iDSBt=qkO_sNqduU)(L<jIqdA3s9$LeeoAq~cE&Mh1rK3_2japgh6O
z@t<Kmr;Nvj1qYisgtcN$1Sm2v@Uq(Vcx+sBv|GYB>&}V72ZuYk?Va9qd{90)StB@(
z<>W;b=ZRXJhk86WFY}r%m2P!JGWf}fc{;gVTUQ7tUu<y?R&zaByyEh7$6hzxol};a
zpA|Yk>+6jv8P}U5t=n=fzdk$Pk$QBQ>+h=%A~t2OG^^RYQTOVO=)1S3?))wlx1^T)
z5--p6M_2dM9DL+c6=9gTH?`Z0?a%fPca9ga{-3KOxWs>6r2YK8e@dS{pIh><uPkQg
zXOClruJfnX-ujxgZY%HfHqBpp>HS_G|4ln#wf+9KE$Qm<J#)&l?`)sTZ+|b^_}PSw
z)8%~g_U<UWdEv10|GL`P-=AJQSUY>(t@8Chm%aSCY5%Uj)~CNeIsN{b-MwwUzOHUf
z6;I!C^V*$^tDe)MlYdCM&nmxqa=LuerJw3@sV^GC7Bp@TbXP16yeWRAPH>ZGWbW6y
z%NKewXsO49Yc6}fpyvPIB@cSF&WX>@lU>9i-=XJZ64}k~mGL;!%J=!gi9SUfcRLKG
zok(tT@XA<P&$sEvjeg5rM;25&uxUP<{HpT$%y9Lv%7>Hu|7}yw3a`4MI&<Gq&F2#@
zxvor}AUg5H^2U;TmJfQ$*LEt;%T`_aVqws!oAX=Mf;u%8<!+l9o0YTe#?thKYb`XV
z<ge45H>poFefjdFvKuK&a;&6QOfoO}FR{%3%9X6uYnASHY3J_x7PWfCgk7&@ty#aU
zQ?v90)2k($*1W8eW7yvHZ1rY--_x(RTnoFcy>@q&_t{CRdQu6CS9E4;&fWJ+>TTug
zx)$B77t&6@-F>_2cHW+?=e*BWh70Xb%*^7lQr!3T*6p>se=F_L-pjMxyWsGZH179@
zIZvAvPWzI$ZNaol&sOVgW7DtEF6;<p*?3s7c=d+kvhsU056q6!Hae_&z31Yt+hTi+
zPHPI6%{cw8Zq4emF3M_#r$j#g(Qf0fzm|J3z<uq6!wS!%^!5g&ul;(~<Nmd`TcX=_
xw`H-2+I+f^v3&0(|4((Rzuhi~o|1c~<oekOYddeQ{eG|JJL}u;4vbeBtN{_~0{#F1

diff --git a/web/root/telnet/Documentation/Source/images/variables.gif b/web/root/telnet/Documentation/Source/images/variables.gif
deleted file mode 100644
index 6e2a42d940e367265e076bba834884a633461d3c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1241
zcmZ?wbhEHb%wbSt_|5<U#taN;3=A_F7|t*-{AVy`U@$gjFivAIp2=W*hQathNHWcs
zAuWv|Z6-t78HTj~Ahk1%8D^$2%$&(E^9;kx{~)brj2X_PF`SvnaOModng0y`85sT>
zGyG3u_&<~3{~3n=|3L<Wj7~FVm}$&##+czh$R1;3W8*Yq<C(_BXN-;igRD(6Hcm@3
zPMc|*cE&jEKgbm`jg4of8PA+)JoAk4%>N)qoiR2(lV*Hortz6G#%KN;|7S4%Z*2TO
z&G`RJ<Ns%j|Nl<|dC52p<mj1cAZPst`2b{7S{lf#Gik>EK^{soP6L6NX=!KD(*A?|
zH`6$6W?I_JnQ1f6q|N*f^6nX9kmQ+}X=l!)o%x^kpCRqPaoYd1wEr{H{+~(v|9>VZ
z?2KoELTBbokpKUK!Up7}w3#4BpP6a=9~4v|i_>O;j6O3n?LR2wK+<V5LEy~Hng2ln
zdBzxI?U|V~&zzZg=Ksw93^V^5&-|Y@^Z(45|If_)|Njgql8n!QqG9G4P(c3&g*qtg
z(m+~4!SNpy32DY>Kwg@82IT1fpg5Un4D!LunP)&o{|80U8Do%#K>Raj&ip^~pW)1Z
z<1_!$&itQw=Kq;9|Nny`5FGs=SAb&_6rbQo0yzpC?x5%e2PMdj;LriZHrT@;CxiU}
ziu^OiprAT46J#*h=>PvgvGo5x$WLIa7<hSkrKF@ZG&D?2O`V;c0|EkKV`H<kvnwhp
z+S=MCPoBJR;lg$6*6rTC`{c=!*REZA^5n_Kk01Y21{8m?FfuS)XV3xV1W=w};P}t5
zo>RtS!-9j&9Ku>LCpIiR+%6!@G(#d#si8~4SZvOTkB<)a$(eFJ>6p0aM5{vZtUDf`
zo^0(?4c}$5!szMoY3hk?S1c!|`p&US|5RhK<+;Obw@#+g9VVJ5mI!p4P05;~71C|A
z+-z-@=)<rk|GjmgH?F4MoE>>kX{lvcPSo<;n{{hD%~TF=4R=5GH8WiI<l6MFkM>%b
z=-l7u9&N_^C+KPHVV`=ht*eTQVov70bepzE;zRuDT*K2(_ssnA@M1Hovfm$(l86%#
ziRtUKqf65!uWP&}xA*ercgOM@AM0@4+<5K5_CRHEzuWqc9__ExuFc+29QEQ_{Cc(B
z)AM7m)vgrUCcb{>_oA0Y2N#F)%k6x5iEHJ_`S%O=)n-k4dz<gpzdavboRciMU#q~Y
z5%0@(&0U~Q=Hm28HRpKQ6)M=mPCRfCi@TA~Bz-IVzW=YkA`g;f(_SdFNlvp^(8XTV
zmehT0qj-Fy<g?P=PN}*dN<9n`Kkk<?&E4_1#_X88La%t$ibWlqvNx4mT-A;)>@JLy
ycskYm8HdtD-(MZ7(|+=1sF=o2yP4XWw(O?x^sH?!pUo~frulqM$t?*125SJbRD16L

diff --git a/web/root/telnet/Documentation/Source/images/yellow-ball-small.gif b/web/root/telnet/Documentation/Source/images/yellow-ball-small.gif
deleted file mode 100644
index 8e5f57cdfcbfbd4ccd3504bdae10cbc9215ef8ca..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 255
zcmZ?wbhEHbWMg1s_{zYrfB*hN$Eweqb2@iU;N-dFYu6Zl|7Upqp5gE@hGWO7kDp`s
z|6kz$AH(1OIUYY}m_CQ$|Er$=PnLfFFR*tnL;n<prWS$U{}s<)<7jDN2#;d8b&p~F
zR))A_h9k%JZr{tWd@Vy%B!ib1LvgV{VKIZN7sH%6UgypwXJ<2X_89*E&p<g){K>)y
sGDQbOfc(V3Cgadhz@e~&ha-_oX=Vt6)LFL4i~$<TL7p0?a+(>e0dEFQiU0rr

diff --git a/web/root/telnet/Documentation/Source/images/yellow-ball.gif b/web/root/telnet/Documentation/Source/images/yellow-ball.gif
deleted file mode 100644
index 2b8c0bb3d6baad5fdb28a878b33c265daeafe9d4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 925
zcmZ?wbhEHb<YC}p_|Cxa|Nnmm28Kh24sGAQJuNNm%a<=R_AlDMfB*C6&-3&185sUA
zTC_-3G<wdQIbmU83=A9W`Kwb>Qlc3cDhjfrg2S~GyA9<x=a(&D_|GuEJ&ECey{d{A
z!~g%9s`-I|J>G#E7jL|N_TkU12QF^ky7kn_Q-uy@nOX{0E?>z_Nsi^`n>Txoqnf4>
zGf!W0!`-`g*Tk70I#zw=oYT2;0w>QUU%STe`#;0;_Y8-RF&sNqef%85|NjEhrcL|*
z$ME-mj>pd#rq5yc|ElNzlcnGP3+&y?&_9LY%$YMyEdsy)E1ti`(bB>Y9>s9$9>e;r
z3~|W}M~>~?zL#P7T85}d1}`s$;$nfqVg^?)hB<S*&Yern&SvQBF+5h?bIvLIoPgK4
z<mzh-PX8H#{xf*JXK*^kV0o;1$~gwh{{m6}4Xgii1U+YPn!}*^zvtTjrSJa>1np(8
zoWh{lB4GGmG5H#YQwxJ;6obS)2B)nIhRF;P$M){s%V7BbKf_uE!zcztF9wNX0mEVt
zLtu_q^10-eYzD<1L$93Va~urs-@SXM@9+QrI>Xm{n=ftYxwTZ`e|^BOhNAzj3}<6v
zHYq7B2-5w3;PkAsMoQgE*?r2P+4>AA4Ds_8I6Sye|NjaD!`bSLcPUffx;1T)lq{HO
zbY@rR<+}^+vNN0u%PxBG<A2rHQxA&M{~y|&nVD&5@?U`CKL^8e4u&~B%l|Ju_FqEa
zKQqH#R)#4e0xcSf|M@wtu`oojF>GaLNM>VL%fJ}Lz~ChyP|U(m%*Np5?KLMg`5ePg
z28usf7#SGY8FWCN0Obh=jw%Lz4jGRP3Oy1=DMuOt3)p&XDzk3vFickHlc_BE@Z#a)
zWBf8q$xmJ~s&oiQId@11YAkl+mvf)M^K+sL=V?{DX-^WIHg}$7Hf-amWNJ+l(F~p=
vbHMQl`$UzHSs@dXQXh!wL`?CxIV*KRkWBCtjhRWVnvR><&uH;-FjxZsA2~jV

diff --git a/web/root/telnet/Documentation/Source/index-all.html b/web/root/telnet/Documentation/Source/index-all.html
deleted file mode 100644
index 7986cb5770..0000000000
--- a/web/root/telnet/Documentation/Source/index-all.html
+++ /dev/null
@@ -1,801 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:31 CEST 1999 -->
-<TITLE>
-: Index
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Index</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<A HREF="#_A_">A</A> <A HREF="#_B_">B</A> <A HREF="#_C_">C</A> <A HREF="#_D_">D</A> <A HREF="#_E_">E</A> <A HREF="#_F_">F</A> <A HREF="#_G_">G</A> <A HREF="#_H_">H</A> <A HREF="#_I_">I</A> <A HREF="#_K_">K</A> <A HREF="#_L_">L</A> <A HREF="#_M_">M</A> <A HREF="#_N_">N</A> <A HREF="#_P_">P</A> <A HREF="#_R_">R</A> <A HREF="#_S_">S</A> <A HREF="#_T_">T</A> <A HREF="#_U_">U</A> <A HREF="#_V_">V</A> <A HREF="#_W_">W</A> <HR>
-<A NAME="_A_"><!-- --></A><H2>
-<B>A</B></H2>
-<DL>
-<DT><A HREF="display/vt320.html#addNotify()"><B>addNotify()</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>Initialize terminal.
-<DT><A HREF="modules/BSXModule.html#addNotify()"><B>addNotify()</B></A> - 
-Method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>Adds two Buttons to the Panel and instanciates a BSXWindow.
-<DT><A HREF="modules/MudConnector.html#addNotify()"><B>addNotify()</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>when newly added try to load the mudlist using the parameter "mudlist"
-<DT><A HREF="modules/ButtonBar.html#addNotify()"><B>addNotify()</B></A> - 
-Method in class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>create the buttonbar from the parameter list.
-<DT><A HREF="telnet.html#address"><B>address</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>The host address to connect to.
-<DT><A HREF="appWrapper.html#appletResize(int, int)"><B>appletResize(int, int)</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>This method is called when the applet want's to be resized.
-<DT><A HREF="appWrapper.html"><B>appWrapper</B></A> - class <A HREF="appWrapper.html">appWrapper</A>.<DD>The appWrapper is thought to make the applet itself independent from
- the original context.<DT><A HREF="appWrapper.html#appWrapper()"><B>appWrapper()</B></A> - 
-Constructor for class <A HREF="appWrapper.html">appWrapper</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TelnetWrapper.html#available()"><B>available()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Returns bytes available to be read.
-<DT><A HREF="socket/TelnetIO.html#available()"><B>available()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Returns bytes available to be read.
-</DL>
-<HR>
-<A NAME="_B_"><!-- --></A><H2>
-<B>B</B></H2>
-<DL>
-<DT><A HREF="display/CharDisplay.html#BOLD"><B>BOLD</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Make character bold.
-<DT><A HREF="modules/BSXModule.html"><B>BSXModule</B></A> - class modules.<A HREF="modules/BSXModule.html">BSXModule</A>.<DD>The ultimate BSX module implements most of the common used
- BSX controll sequences.<DT><A HREF="modules/BSXModule.html#BSXModule()"><B>BSXModule()</B></A> - 
-Constructor for class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>&nbsp;
-<DT><A HREF="modules/ButtonBar.html"><B>ButtonBar</B></A> - class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>.<DD>This class implements a programmable button bar.<DT><A HREF="modules/ButtonBar.html#ButtonBar()"><B>ButtonBar()</B></A> - 
-Constructor for class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>&nbsp;
-</DL>
-<HR>
-<A NAME="_C_"><!-- --></A><H2>
-<B>C</B></H2>
-<DL>
-<DT><A HREF="display/CharDisplay.html"><B>CharDisplay</B></A> - class display.<A HREF="display/CharDisplay.html">CharDisplay</A>.<DD>A simple character display.<DT><A HREF="display/CharDisplay.html#CharDisplay()"><B>CharDisplay()</B></A> - 
-Constructor for class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Create a character display with size 80x24 and Font "Courier", size 12.
-<DT><A HREF="display/CharDisplay.html#CharDisplay(int, int)"><B>CharDisplay(int, int)</B></A> - 
-Constructor for class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Create a character display with specific size, Font is "Courier", size 12.
-<DT><A HREF="display/CharDisplay.html#CharDisplay(int, int, java.lang.String, int)"><B>CharDisplay(int, int, String, int)</B></A> - 
-Constructor for class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Create a character display with specific size, font and font size.
-<DT><A HREF="display/CharDisplay.html#CharDisplay(java.lang.String, int)"><B>CharDisplay(String, int)</B></A> - 
-Constructor for class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Create a character display with 80x24 and specific font and font size.
-<DT><A HREF="CharDisplayTest.html"><B>CharDisplayTest</B></A> - class <A HREF="CharDisplayTest.html">CharDisplayTest</A>.<DD>CharDisplayTest -- a test applet to show the display/CharDisplay features
- --<DT><A HREF="CharDisplayTest.html#CharDisplayTest()"><B>CharDisplayTest()</B></A> - 
-Constructor for class <A HREF="CharDisplayTest.html">CharDisplayTest</A>
-<DD>&nbsp;
-<DT><A HREF="modules/BSXModule.html#clientVersion"><B>clientVersion</B></A> - 
-Variable in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>Client Version
-<DT><A HREF="telnet.html#connect()"><B>connect()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Connect to the specified host and port but don't break existing 
- connections.
-<DT><A HREF="telnet.html#connect(java.lang.String)"><B>connect(String)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Connect to the specified host and port but don't break existing 
- connections.
-<DT><A HREF="socket/TelnetIO.html#connect(java.lang.String)"><B>connect(String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Connect to the remote host at the default telnet port (23).
-<DT><A HREF="telnet.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Connect to the specified host and port but don't break existing 
- connections.
-<DT><A HREF="socket/TelnetIO.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Connect to the remote host at the specified port.
-<DT><A HREF="modules/BSXModule.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>do nothing
-<DT><A HREF="modules/Module.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in interface modules.<A HREF="modules/Module.html">Module</A>
-<DD>Connected to the remote host.
-<DT><A HREF="modules/TextLabel.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class modules.<A HREF="modules/TextLabel.html">TextLabel</A>
-<DD>Do nothing upon connect.
-<DT><A HREF="modules/MudConnector.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>dummy methods
-<DT><A HREF="modules/Script.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class modules.<A HREF="modules/Script.html">Script</A>
-<DD>Configure the script module by reading the script PARAMeter.
-<DT><A HREF="modules/ButtonBar.html#connect(java.lang.String, int)"><B>connect(String, int)</B></A> - 
-Method in class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>If the applet connects this method is called.
-</DL>
-<HR>
-<A NAME="_D_"><!-- --></A><H2>
-<B>D</B></H2>
-<DL>
-<DT><A HREF="display/CharDisplay.html#debug"><B>debug</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Enable debug messages.
-<DT><A HREF="socket/TelnetWrapper.html#debug"><B>debug</B></A> - 
-Variable in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Set to true for System.out.println debugging.
-<DT><A HREF="display/CharDisplay.html#deleteArea(int, int, int, int)"><B>deleteArea(int, int, int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Delete a rectangular portion of the screen.
-<DT><A HREF="display/CharDisplay.html#deleteChar(int, int)"><B>deleteChar(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Delete a character at a given position on the screen.
-<DT><A HREF="display/CharDisplay.html#deleteLine(int)"><B>deleteLine(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Delete a line at a specific position.
-<DT><A HREF="telnet.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Disconnect from the remote host.
-<DT><A HREF="socket/TelnetWrapper.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Ends the telnet connection.
-<DT><A HREF="socket/TelnetIO.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Disconnect from remote host.
-<DT><A HREF="modules/BSXModule.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>do nothing
-<DT><A HREF="modules/Module.html#disconnect()"><B>disconnect()</B></A> - 
-Method in interface modules.<A HREF="modules/Module.html">Module</A>
-<DD>Disconnect from the host.
-<DT><A HREF="modules/TextLabel.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class modules.<A HREF="modules/TextLabel.html">TextLabel</A>
-<DD>Do nothing upon disconnecton.
-<DT><A HREF="modules/MudConnector.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>&nbsp;
-<DT><A HREF="modules/Script.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class modules.<A HREF="modules/Script.html">Script</A>
-<DD>Get notified of disconnection.
-<DT><A HREF="modules/ButtonBar.html#disconnect()"><B>disconnect()</B></A> - 
-Method in class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>Get notified of disconnection.
-<DT><A HREF="display/package-summary.html">display</A> - package display<DD>&nbsp;<DT><A HREF="display/SoftFont.html#drawChar(java.awt.Graphics, char, int, int, int, int)"><B>drawChar(Graphics, char, int, int, int, int)</B></A> - 
-Method in class display.<A HREF="display/SoftFont.html">SoftFont</A>
-<DD>&nbsp;
-</DL>
-<HR>
-<A NAME="_E_"><!-- --></A><H2>
-<B>E</B></H2>
-<DL>
-<DT><A HREF="telnet.html#emulation"><B>emulation</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>Emulation type (default is vt320).
-</DL>
-<HR>
-<A NAME="_F_"><!-- --></A><H2>
-<B>F</B></H2>
-<DL>
-<DT><A HREF="socket/TelnetWrapper.html#finalize()"><B>finalize()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Ends the telnet connection.
-</DL>
-<HR>
-<A NAME="_G_"><!-- --></A><H2>
-<B>G</B></H2>
-<DL>
-<DT><A HREF="telnet.html#getAppletInfo()"><B>getAppletInfo()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Retrieve the current version of the applet.
-<DT><A HREF="appWrapper.html#getAppletInfo()"><B>getAppletInfo()</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>Give information about the applet.
-<DT><A HREF="display/CharDisplay.html#getAttributes(int, int)"><B>getAttributes(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get the attributes for the specified position.
-<DT><A HREF="display/CharDisplay.html#getBottomMargin()"><B>getBottomMargin()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get the bottom scroll margin.
-<DT><A HREF="display/CharDisplay.html#getBufferSize()"><B>getBufferSize()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Retrieve current scrollback buffer size.
-<DT><A HREF="display/CharDisplay.html#getChar(int, int)"><B>getChar(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get the character at the specified position.
-<DT><A HREF="display/CharDisplay.html#getColumns()"><B>getColumns()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get amount of columns on the screen.
-<DT><A HREF="display/CharDisplay.html#getCursorPos()"><B>getCursorPos()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get the current cursor position.
-<DT><A HREF="display/CharDisplay.html#getMaxBufferSize()"><B>getMaxBufferSize()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Retrieve maximum buffer Size.
-<DT><A HREF="telnet.html#getParameter(java.lang.String)"><B>getParameter(String)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>We override the Applet method getParameter() to be able to handle 
- parameters even as application.
-<DT><A HREF="telnet.html#getParameterInfo()"><B>getParameterInfo()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Retrieve parameter tag information.
-<DT><A HREF="appWrapper.html#getParameterInfo()"><B>getParameterInfo()</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>Give information about the appWrapper and the applet loaded.
-<DT><A HREF="display/Terminal.html#getParameterInfo()"><B>getParameterInfo()</B></A> - 
-Method in class display.<A HREF="display/Terminal.html">Terminal</A>
-<DD>Get the specific parameter info for the emulation.
-<DT><A HREF="display/vt320.html#getParameterInfo()"><B>getParameterInfo()</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#getRows()"><B>getRows()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get amount of rows on the screen.
-<DT><A HREF="display/Terminal.html#getSize()"><B>getSize()</B></A> - 
-Method in class display.<A HREF="display/Terminal.html">Terminal</A>
-<DD>Return the current size of the terminal in characters.
-<DT><A HREF="display/vt320.html#getSize()"><B>getSize()</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/Terminal.html#getTerminalType()"><B>getTerminalType()</B></A> - 
-Method in class display.<A HREF="display/Terminal.html">Terminal</A>
-<DD>Return actual terminal type identifier.
-<DT><A HREF="display/vt320.html#getTerminalType()"><B>getTerminalType()</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#getTopMargin()"><B>getTopMargin()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get the top scroll margin.
-<DT><A HREF="display/CharDisplay.html#getWindowBase()"><B>getWindowBase()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Get the current window base.
-</DL>
-<HR>
-<A NAME="_H_"><!-- --></A><H2>
-<B>H</B></H2>
-<DL>
-<DT><A HREF="appWrapper.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>Handle button events.
-<DT><A HREF="CharDisplayTest.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class <A HREF="CharDisplayTest.html">CharDisplayTest</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Handle mouse events for copy & paste
-<DT><A HREF="display/vt320.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>Handle events for the terminal.
-<DT><A HREF="modules/BSXModule.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>Java 1.0 eventhandling routines.
-<DT><A HREF="modules/MudConnector.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>handle list selection, connect, disconnect and refresh button
-<DT><A HREF="modules/ButtonBar.html#handleEvent(java.awt.Event)"><B>handleEvent(Event)</B></A> - 
-Method in class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>&nbsp;
-</DL>
-<HR>
-<A NAME="_I_"><!-- --></A><H2>
-<B>I</B></H2>
-<DL>
-<DT><A HREF="telnet.html#init()"><B>init()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Initialize applet.
-<DT><A HREF="appWrapper.html#init()"><B>init()</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>Applet initialization.
-<DT><A HREF="CharDisplayTest.html#init()"><B>init()</B></A> - 
-Method in class <A HREF="CharDisplayTest.html">CharDisplayTest</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#INSERT"><B>INSERT</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>The Insert key.
-<DT><A HREF="display/CharDisplay.html#insertChar(int, int, char, int)"><B>insertChar(int, int, char, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Insert a character at a specific position on the screen.
-<DT><A HREF="display/CharDisplay.html#insertLine(int)"><B>insertLine(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Insert a blank line at a specific position.
-<DT><A HREF="display/CharDisplay.html#insertLine(int, boolean)"><B>insertLine(int, boolean)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Insert a blank line at a specific position.
-<DT><A HREF="display/CharDisplay.html#insertLine(int, int)"><B>insertLine(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Insert blank lines at a specific position.
-<DT><A HREF="display/CharDisplay.html#insertLine(int, int, boolean)"><B>insertLine(int, int, boolean)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Insert blank lines at a specific position.
-<DT><A HREF="display/CharDisplay.html#insets()"><B>insets()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>The insets of the character display define the border.
-<DT><A HREF="display/SoftFont.html#inSoftFont(char)"><B>inSoftFont(char)</B></A> - 
-Method in class display.<A HREF="display/SoftFont.html">SoftFont</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#INVERT"><B>INVERT</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Invert character.
-</DL>
-<HR>
-<A NAME="_K_"><!-- --></A><H2>
-<B>K</B></H2>
-<DL>
-<DT><A HREF="display/vt320.html#KEYCAPS_LOCK"><B>KEYCAPS_LOCK</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#KEYINSERT"><B>KEYINSERT</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#KEYNUM_LOCK"><B>KEYNUM_LOCK</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#KEYPAUSE"><B>KEYPAUSE</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#KEYPRINT_SCREEN"><B>KEYPRINT_SCREEN</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#KEYSCROLL_LOCK"><B>KEYSCROLL_LOCK</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-</DL>
-<HR>
-<A NAME="_L_"><!-- --></A><H2>
-<B>L</B></H2>
-<DL>
-<DT><A HREF="socket/TelnetWrapper.html#login(java.lang.String, java.lang.String)"><B>login(String, String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Logs in as a particular user and password.
-</DL>
-<HR>
-<A NAME="_M_"><!-- --></A><H2>
-<B>M</B></H2>
-<DL>
-<DT><A HREF="telnet.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
-Static method in class <A HREF="telnet.html">telnet</A>
-<DD>The main function is called on startup of the application.
-<DT><A HREF="proxy.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
-Static method in class <A HREF="proxy.html">proxy</A>
-<DD>This method is called when the application is run on the commandline.
-<DT><A HREF="socket/TelnetWrapper.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
-Static method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Telnet test driver.
-<DT><A HREF="modules/BSXModule.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
-Static method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>a main for test-purposes
-<DT><A HREF="display/vt320.html#map_cp850_unicode(char)"><B>map_cp850_unicode(char)</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#markLine(int, int)"><B>markLine(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Mark lines to be updated with redraw().
-<DT><A HREF="modules/Module.html"><B>Module</B></A> - interface modules.<A HREF="modules/Module.html">Module</A>.<DD>Modules must implement this interface to be detected as valid modules<DT><A HREF="modules/package-summary.html">modules</A> - package modules<DD>&nbsp;<DT><A HREF="telnet.html#modules"><B>modules</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>Dynamically loaded modules are stored here.
-<DT><A HREF="modules/MudConnector.html"><B>MudConnector</B></A> - class modules.<A HREF="modules/MudConnector.html">MudConnector</A>.<DD>A specially designed module for the 
- <A HREF="http://www.mudconnect.com/">MUD Connector</A>.<DT><A HREF="modules/MudConnector.html#MudConnector()"><B>MudConnector()</B></A> - 
-Constructor for class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>&nbsp;
-</DL>
-<HR>
-<A NAME="_N_"><!-- --></A><H2>
-<B>N</B></H2>
-<DL>
-<DT><A HREF="display/CharDisplay.html#NORMAL"><B>NORMAL</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Make character normal.
-<DT><A HREF="display/CharDisplay.html#notbold(java.awt.Color)"><B>notbold(Color)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>&nbsp;
-<DT><A HREF="telnet.html#notifyStatus(java.util.Vector)"><B>notifyStatus(Vector)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>This method is called when telnet needs to be notified of status changes.
-<DT><A HREF="socket/TelnetIO.html#notifyStatus(java.util.Vector)"><B>notifyStatus(Vector)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Notify about current telnet status.
-<DT><A HREF="socket/StatusPeer.html#notifyStatus(java.util.Vector)"><B>notifyStatus(Vector)</B></A> - 
-Method in interface socket.<A HREF="socket/StatusPeer.html">StatusPeer</A>
-<DD>This method is called for the peer of the TelnetIO class if there is
- a statuschange.
-</DL>
-<HR>
-<A NAME="_P_"><!-- --></A><H2>
-<B>P</B></H2>
-<DL>
-<DT><A HREF="appWrapper.html#paint(java.awt.Graphics)"><B>paint(Graphics)</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>Write a message to the applet area.
-<DT><A HREF="display/CharDisplay.html#paint(java.awt.Graphics)"><B>paint(Graphics)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Paint the current screen.
-<DT><A HREF="telnet.html#params"><B>params</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>This Hashtable contains information retrievable by getParameter() in case
- the program is run as an application and the AppletStub is missing.
-<DT><A HREF="telnet.html#port"><B>port</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>The port number (default ist 23).
-<DT><A HREF="display/CharDisplay.html#preferredSize()"><B>preferredSize()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Return the preferred Size of the character display.
-<DT><A HREF="proxy.html"><B>proxy</B></A> - class <A HREF="proxy.html">proxy</A>.<DD>proxy class -- implements a proxy server to redirect network access<DT><A HREF="telnet.html#proxy"><B>proxy</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>The proxy ip address.
-<DT><A HREF="proxy.html#proxy(int, java.lang.String, int)"><B>proxy(int, String, int)</B></A> - 
-Constructor for class <A HREF="proxy.html">proxy</A>
-<DD>Create a server socket and start listening on the local port.
-<DT><A HREF="telnet.html#proxyport"><B>proxyport</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>The proxy port number.
-<DT><A HREF="display/Terminal.html#putChar(char)"><B>putChar(char)</B></A> - 
-Method in class display.<A HREF="display/Terminal.html">Terminal</A>
-<DD>Put a character on the screen.
-<DT><A HREF="display/vt320.html#putChar(char)"><B>putChar(char)</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/vt320.html#putChar(char, boolean)"><B>putChar(char, boolean)</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#putChar(int, int, char)"><B>putChar(int, int, char)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Put a character on the screen with normal font and outline.
-<DT><A HREF="display/CharDisplay.html#putChar(int, int, char, int)"><B>putChar(int, int, char, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Put a character on the screen with specific font and outline.
-<DT><A HREF="display/CharDisplay.html#putString(int, int, java.lang.String)"><B>putString(int, int, String)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Put a String at a specific position.
-<DT><A HREF="display/CharDisplay.html#putString(int, int, java.lang.String, int)"><B>putString(int, int, String, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Put a String at a specific position giving all characters the same
- attributes.
-<DT><A HREF="display/Terminal.html#putString(java.lang.String)"><B>putString(String)</B></A> - 
-Method in class display.<A HREF="display/Terminal.html">Terminal</A>
-<DD>Put a character on the screen.
-<DT><A HREF="display/vt320.html#putString(java.lang.String)"><B>putString(String)</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>Put String at current cursor position.
-</DL>
-<HR>
-<A NAME="_R_"><!-- --></A><H2>
-<B>R</B></H2>
-<DL>
-<DT><A HREF="socket/TelnetWrapper.html#receive()"><B>receive()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Returns a String from the telnet connection.
-<DT><A HREF="socket/TelnetIO.html#receive()"><B>receive()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Read data from the remote host.
-<DT><A HREF="modules/BSXModule.html#receive(java.lang.String)"><B>receive(String)</B></A> - 
-Method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>This method is called by the telnet, so that we can parse the String.
-<DT><A HREF="modules/Module.html#receive(java.lang.String)"><B>receive(String)</B></A> - 
-Method in interface modules.<A HREF="modules/Module.html">Module</A>
-<DD>Receive data from somewhere.
-<DT><A HREF="modules/TextLabel.html#receive(java.lang.String)"><B>receive(String)</B></A> - 
-Method in class modules.<A HREF="modules/TextLabel.html">TextLabel</A>
-<DD>Do nothing when receiving text.
-<DT><A HREF="modules/MudConnector.html#receive(java.lang.String)"><B>receive(String)</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>&nbsp;
-<DT><A HREF="modules/Script.html#receive(java.lang.String)"><B>receive(String)</B></A> - 
-Method in class modules.<A HREF="modules/Script.html">Script</A>
-<DD>This method is called when data is received.
-<DT><A HREF="modules/ButtonBar.html#receive(java.lang.String)"><B>receive(String)</B></A> - 
-Method in class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>This module does not take any input.
-<DT><A HREF="socket/TelnetWrapper.html#receiveBytes()"><B>receiveBytes()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Returns a byte array.
-<DT><A HREF="socket/TelnetWrapper.html#receiveUntil(java.lang.String)"><B>receiveUntil(String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Returns all data received up until a certain token.
-<DT><A HREF="socket/TelnetWrapper.html#receiveUntil(java.lang.String, long)"><B>receiveUntil(String, long)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Returns all data received up until a certain token.
-<DT><A HREF="display/CharDisplay.html#redraw()"><B>redraw()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Redraw marked lines.
-<DT><A HREF="appWrapper.html#reshape(int, int, int, int)"><B>reshape(int, int, int, int)</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>reshape the applet and ourself
-<DT><A HREF="display/CharDisplay.html#reshape(int, int, int, int)"><B>reshape(int, int, int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Reshape character display according to resize strategy.
-<DT><A HREF="display/CharDisplay.html#RESIZE_FONT"><B>RESIZE_FONT</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Resize the font to the new screensize.
-<DT><A HREF="display/CharDisplay.html#RESIZE_NONE"><B>RESIZE_NONE</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Do nothing when the container is resized.
-<DT><A HREF="display/CharDisplay.html#RESIZE_SCREEN"><B>RESIZE_SCREEN</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Resize the width and height of the characterscreen.
-<DT><A HREF="telnet.html#run()"><B>run()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Try to read data from the sockets and put it on the terminal.
-<DT><A HREF="appWrapper.html#run()"><B>run()</B></A> - 
-Method in class <A HREF="appWrapper.html">appWrapper</A>
-<DD>Load the applet finally.
-<DT><A HREF="proxy.html#run()"><B>run()</B></A> - 
-Method in class <A HREF="proxy.html">proxy</A>
-<DD>Cycle around until an error occurs and wait for incoming connections.
-<DT><A HREF="modules/MudConnector.html#run()"><B>run()</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>The body of the thread opens a URLConnection with the address given as
- parameter "mudlist" and downloads it.
-</DL>
-<HR>
-<A NAME="_S_"><!-- --></A><H2>
-<B>S</B></H2>
-<DL>
-<DT><A HREF="modules/Script.html"><B>Script</B></A> - class modules.<A HREF="modules/Script.html">Script</A>.<DD>A very simple scripting module.<DT><A HREF="modules/Script.html#Script()"><B>Script()</B></A> - 
-Constructor for class modules.<A HREF="modules/Script.html">Script</A>
-<DD>&nbsp;
-<DT><A HREF="display/CharDisplay.html#SCROLL_DOWN"><B>SCROLL_DOWN</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Scroll down when inserting a line.
-<DT><A HREF="display/CharDisplay.html#SCROLL_UP"><B>SCROLL_UP</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Scroll up when inserting a line.
-<DT><A HREF="socket/TelnetIO.html#send(byte)"><B>send(byte)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TelnetWrapper.html#send(byte[])"><B>send(byte[])</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Sends bytes over the telnet connection.
-<DT><A HREF="socket/TelnetIO.html#send(byte[])"><B>send(byte[])</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Send data to the remote host.
-<DT><A HREF="telnet.html#send(java.lang.String)"><B>send(String)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Send a String to the remote host.
-<DT><A HREF="display/TerminalHost.html#send(java.lang.String)"><B>send(String)</B></A> - 
-Method in interface display.<A HREF="display/TerminalHost.html">TerminalHost</A>
-<DD>Send a string to the host and return if it was received successfully.
-<DT><A HREF="display/vt320.html#send(java.lang.String)"><B>send(String)</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>Dummy method to handle input events (String).
-<DT><A HREF="socket/TelnetWrapper.html#send(java.lang.String)"><B>send(String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Sends a String to the remote host.
-<DT><A HREF="socket/TelnetWrapper.html#sendLine(java.lang.String)"><B>sendLine(String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Sends a line to the remote host, returns all data before the prompt.
-<DT><A HREF="display/CharDisplay.html#setBorder(int, boolean)"><B>setBorder(int, boolean)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set the border thickness and the border type.
-<DT><A HREF="display/CharDisplay.html#setBottomMargin(int)"><B>setBottomMargin(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set the bottom scroll margin for the screen.
-<DT><A HREF="display/CharDisplay.html#setBufferSize(int)"><B>setBufferSize(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set scrollback buffer size.
-<DT><A HREF="display/CharDisplay.html#setCursorPos(int, int)"><B>setCursorPos(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Puts the cursor at the specified position.
-<DT><A HREF="socket/TelnetWrapper.html#setDefaultPrompt(java.lang.String)"><B>setDefaultPrompt(String)</B></A> - 
-Static method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Sets the default prompt used by all TelnetWrappers.
-<DT><A HREF="modules/BSXModule.html#setLoader(java.lang.Object)"><B>setLoader(Object)</B></A> - 
-Method in class modules.<A HREF="modules/BSXModule.html">BSXModule</A>
-<DD>register o as our parent
-<DT><A HREF="modules/Module.html#setLoader(java.lang.Object)"><B>setLoader(Object)</B></A> - 
-Method in interface modules.<A HREF="modules/Module.html">Module</A>
-<DD>Set the loader of the module.
-<DT><A HREF="modules/TextLabel.html#setLoader(java.lang.Object)"><B>setLoader(Object)</B></A> - 
-Method in class modules.<A HREF="modules/TextLabel.html">TextLabel</A>
-<DD>Set the applet as module loader and configure.
-<DT><A HREF="modules/MudConnector.html#setLoader(java.lang.Object)"><B>setLoader(Object)</B></A> - 
-Method in class modules.<A HREF="modules/MudConnector.html">MudConnector</A>
-<DD>setLoader() is called upon start of the parent applet.
-<DT><A HREF="modules/Script.html#setLoader(java.lang.Object)"><B>setLoader(Object)</B></A> - 
-Method in class modules.<A HREF="modules/Script.html">Script</A>
-<DD>Set the applet as module loader
-<DT><A HREF="modules/ButtonBar.html#setLoader(java.lang.Object)"><B>setLoader(Object)</B></A> - 
-Method in class modules.<A HREF="modules/ButtonBar.html">ButtonBar</A>
-<DD>This method is called by our loader to notify us of it.
-<DT><A HREF="socket/TelnetWrapper.html#setLogin(java.lang.String, java.lang.String)"><B>setLogin(String, String)</B></A> - 
-Static method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Sets the default login used by TelnetWrappers.
-<DT><A HREF="socket/TelnetIO.html#setPeer(socket.StatusPeer)"><B>setPeer(StatusPeer)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Set the object to be notified about current status.
-<DT><A HREF="socket/TelnetWrapper.html#setPrompt(java.lang.String)"><B>setPrompt(String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Sets the expected prompt.
-<DT><A HREF="display/CharDisplay.html#setResizeStrategy(int)"><B>setResizeStrategy(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set the strategy when window is resized.
-<DT><A HREF="display/CharDisplay.html#setScrollbar(java.lang.String)"><B>setScrollbar(String)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set the scrollbar position.
-<DT><A HREF="display/CharDisplay.html#setTopMargin(int)"><B>setTopMargin(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set the top scroll margin for the screen.
-<DT><A HREF="display/CharDisplay.html#setWindowBase(int)"><B>setWindowBase(int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Set the current window base.
-<DT><A HREF="display/CharDisplay.html#setWindowSize(int, int)"><B>setWindowSize(int, int)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Change the size of the screen.
-<DT><A HREF="display/CharDisplay.html#size()"><B>size()</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Return the real size in points of the character display.
-<DT><A HREF="socket/package-summary.html">socket</A> - package socket<DD>&nbsp;<DT><A HREF="display/SoftFont.html"><B>SoftFont</B></A> - class display.<A HREF="display/SoftFont.html">SoftFont</A>.<DD>&nbsp;<DT><A HREF="display/SoftFont.html#SoftFont()"><B>SoftFont()</B></A> - 
-Constructor for class display.<A HREF="display/SoftFont.html">SoftFont</A>
-<DD>&nbsp;
-<DT><A HREF="telnet.html#start()"><B>start()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Upon start of the applet try to create a new connection.
-<DT><A HREF="socket/StatusPeer.html"><B>StatusPeer</B></A> - interface socket.<A HREF="socket/StatusPeer.html">StatusPeer</A>.<DD>StatusPeer -- interface for status messages
- --<DT><A HREF="telnet.html#stop()"><B>stop()</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Disconnect when the applet is stopped.
-</DL>
-<HR>
-<A NAME="_T_"><!-- --></A><H2>
-<B>T</B></H2>
-<DL>
-<DT><A HREF="telnet.html"><B>telnet</B></A> - class <A HREF="telnet.html">telnet</A>.<DD>A telnet implementation that supports different terminal emulations.<DT><A HREF="telnet.html#telnet()"><B>telnet()</B></A> - 
-Constructor for class <A HREF="telnet.html">telnet</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TelnetIO.html"><B>TelnetIO</B></A> - class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>.<DD>Implements simple telnet io<DT><A HREF="socket/TelnetIO.html#TelnetIO()"><B>TelnetIO()</B></A> - 
-Constructor for class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TelnetWrapper.html"><B>TelnetWrapper</B></A> - class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>.<DD>Wrapper for a Java Telnet call.<DT><A HREF="socket/TelnetWrapper.html#TelnetWrapper(java.lang.String)"><B>TelnetWrapper(String)</B></A> - 
-Constructor for class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Connects to the default telnet port on the given host.
-<DT><A HREF="socket/TelnetWrapper.html#TelnetWrapper(java.lang.String, int)"><B>TelnetWrapper(String, int)</B></A> - 
-Constructor for class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Connects to a specific telnet port on the given host.
-<DT><A HREF="telnet.html#term"><B>term</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>The terminal emulation (dynamically loaded).
-<DT><A HREF="display/Terminal.html"><B>Terminal</B></A> - class display.<A HREF="display/Terminal.html">Terminal</A>.<DD>Terminal is an abstract emulation class.<DT><A HREF="display/Terminal.html#Terminal()"><B>Terminal()</B></A> - 
-Constructor for class display.<A HREF="display/Terminal.html">Terminal</A>
-<DD>&nbsp;
-<DT><A HREF="display/TerminalHost.html"><B>TerminalHost</B></A> - interface display.<A HREF="display/TerminalHost.html">TerminalHost</A>.<DD>TerminalHost is an interface for the remote (virtual) end of our connection
- to the host computer we are connected to.<DT><A HREF="modules/TextLabel.html"><B>TextLabel</B></A> - class modules.<A HREF="modules/TextLabel.html">TextLabel</A>.<DD>This small module lets you display text somewhere in the applets area.<DT><A HREF="modules/TextLabel.html#TextLabel()"><B>TextLabel()</B></A> - 
-Constructor for class modules.<A HREF="modules/TextLabel.html">TextLabel</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TimedOutException.html"><B>TimedOutException</B></A> - exception socket.<A HREF="socket/TimedOutException.html">TimedOutException</A>.<DD>Exception thrown when a Telnet connection takes too long
- before receiving a specified String token.<DT><A HREF="socket/TimedOutException.html#TimedOutException()"><B>TimedOutException()</B></A> - 
-Constructor for class socket.<A HREF="socket/TimedOutException.html">TimedOutException</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TimedOutException.html#TimedOutException(java.lang.String)"><B>TimedOutException(String)</B></A> - 
-Constructor for class socket.<A HREF="socket/TimedOutException.html">TimedOutException</A>
-<DD>&nbsp;
-<DT><A HREF="telnet.html#tio"><B>tio</B></A> - 
-Variable in class <A HREF="telnet.html">telnet</A>
-<DD>The telnet io methods.
-<DT><A HREF="display/vt320.html#toString()"><B>toString()</B></A> - 
-Method in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>Return the version of the terminal emulation and its display.
-<DT><A HREF="socket/TelnetIO.html#toString()"><B>toString()</B></A> - 
-Method in class socket.<A HREF="socket/TelnetIO.html">TelnetIO</A>
-<DD>Return the version of TelnetIO.
-</DL>
-<HR>
-<A NAME="_U_"><!-- --></A><H2>
-<B>U</B></H2>
-<DL>
-<DT><A HREF="display/CharDisplay.html#UNDERLINE"><B>UNDERLINE</B></A> - 
-Static variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Underline character.
-<DT><A HREF="display/vt320.html#unimap"><B>unimap</B></A> - 
-Static variable in class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-<DT><A HREF="socket/TelnetWrapper.html#unsetLogin()"><B>unsetLogin()</B></A> - 
-Static method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Turns off the default login of TelnetWrappers.
-<DT><A HREF="display/CharDisplay.html#update(java.awt.Graphics)"><B>update(Graphics)</B></A> - 
-Method in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>Update the display.
-</DL>
-<HR>
-<A NAME="_V_"><!-- --></A><H2>
-<B>V</B></H2>
-<DL>
-<DT><A HREF="display/CharDisplay.html#version"><B>version</B></A> - 
-Variable in class display.<A HREF="display/CharDisplay.html">CharDisplay</A>
-<DD>If you need the runtime version, just ask this variable.
-<DT><A HREF="display/vt320.html"><B>vt320</B></A> - class display.<A HREF="display/vt320.html">vt320</A>.<DD>A DEC VT320 Terminal Emulation (includes VT100/220 and ANSI).<DT><A HREF="display/vt320.html#vt320()"><B>vt320()</B></A> - 
-Constructor for class display.<A HREF="display/vt320.html">vt320</A>
-<DD>&nbsp;
-</DL>
-<HR>
-<A NAME="_W_"><!-- --></A><H2>
-<B>W</B></H2>
-<DL>
-<DT><A HREF="socket/TelnetWrapper.html#wait(java.lang.String)"><B>wait(String)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Skip any received data until the token appears.
-<DT><A HREF="socket/TelnetWrapper.html#wait(java.lang.String, long)"><B>wait(String, long)</B></A> - 
-Method in class socket.<A HREF="socket/TelnetWrapper.html">TelnetWrapper</A>
-<DD>Wait for a String or a timeout.
-<DT><A HREF="telnet.html#writeToSocket(java.lang.String)"><B>writeToSocket(String)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Send a String to the remote Host.
-<DT><A HREF="telnet.html#writeToUser(java.lang.String)"><B>writeToUser(String)</B></A> - 
-Method in class <A HREF="telnet.html">telnet</A>
-<DD>Send a String to the users terminal
-</DL>
-<HR>
-<A HREF="#_A_">A</A> <A HREF="#_B_">B</A> <A HREF="#_C_">C</A> <A HREF="#_D_">D</A> <A HREF="#_E_">E</A> <A HREF="#_F_">F</A> <A HREF="#_G_">G</A> <A HREF="#_H_">H</A> <A HREF="#_I_">I</A> <A HREF="#_K_">K</A> <A HREF="#_L_">L</A> <A HREF="#_M_">M</A> <A HREF="#_N_">N</A> <A HREF="#_P_">P</A> <A HREF="#_R_">R</A> <A HREF="#_S_">S</A> <A HREF="#_T_">T</A> <A HREF="#_U_">U</A> <A HREF="#_V_">V</A> <A HREF="#_W_">W</A> 
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Index</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/index.html b/web/root/telnet/Documentation/Source/index.html
deleted file mode 100644
index cc35f0b2e3..0000000000
--- a/web/root/telnet/Documentation/Source/index.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:31 CEST 1999-->
-<TITLE>
-Generated Documentation (Untitled)
-</TITLE>
-</HEAD>
-<FRAMESET cols="20%,80%">
-<FRAMESET rows="30%,70%">
-<FRAME src="overview-frame.html" name="packageListFrame">
-<FRAME src="allclasses-frame.html" name="packageFrame">
-</FRAMESET>
-<FRAME src="overview-summary.html" name="classFrame">
-</FRAMESET>
-<NOFRAMES>
-<H2>
-Frame Alert</H2>
-
-<P>
-This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
-<BR>
-Link to <A HREF="overview-summary.html">Non-frame version.</A></NOFRAMES>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/BSXModule.html b/web/root/telnet/Documentation/Source/modules/BSXModule.html
deleted file mode 100644
index d1f285c8d7..0000000000
--- a/web/root/telnet/Documentation/Source/modules/BSXModule.html
+++ /dev/null
@@ -1,596 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Class  BSXModule
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="../modules/ButtonBar.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="BSXModule.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-modules</FONT>
-<BR>
-Class  BSXModule</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<B>modules.BSXModule</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>BSXModule</B><DT>extends java.awt.Panel<DT>implements <A HREF="../modules/Module.html">Module</A></DL>
-
-<P>
-The ultimate BSX module implements most of the common used
- BSX controll sequences.
- <P>
- Features:<UL>
- <LI>a Statemachine to parse the Strings passed by the telnet
- <LI>a own package named bsx, for Window and Polygon handling
- terrible english because my native language is german
- </UL>
- TODO:<UL>
- <LI>more BSX Sequences
- <LI>Documentaion
- </UL>
-<P>
-<DL>
-<DT><B>Author: </B><DD>Thomas Kriegelstein</DD>
-<DT><B>See Also: </B><DD><A HREF="../serialized-form.html#modules.BSXModule">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="field_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Field Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#clientVersion">clientVersion</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Client Version</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../modules/BSXModule.html#BSXModule()">BSXModule</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#addNotify()">addNotify</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Adds two Buttons to the Panel and instanciates a BSXWindow.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do nothing</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do nothing</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;e)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Java 1.0 eventhandling routines.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;args)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a main for test-purposes</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#receive(java.lang.String)">receive</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called by the telnet, so that we can parse the String.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/BSXModule.html#setLoader(java.lang.Object)">setLoader</A></B>(java.lang.Object&nbsp;o)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;register o as our parent</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-<A NAME="field_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Field Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="clientVersion"><!-- --></A><H3>
-clientVersion</H3>
-<PRE>
-protected java.lang.String <B>clientVersion</B></PRE>
-<DL>
-<DD>Client Version</DL>
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="BSXModule()"><!-- --></A><H3>
-BSXModule</H3>
-<PRE>
-public <B>BSXModule</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="setLoader(java.lang.Object)"><!-- --></A><H3>
-setLoader</H3>
-<PRE>
-public void <B>setLoader</B>(java.lang.Object&nbsp;o)</PRE>
-<DL>
-<DD>register o as our parent<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#setLoader(java.lang.Object)">setLoader</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;host,
-                    int&nbsp;port)</PRE>
-<DL>
-<DD>do nothing<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#connect(java.lang.String, int)">connect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()</PRE>
-<DL>
-<DD>do nothing<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#disconnect()">disconnect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receive(java.lang.String)"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>This method is called by the telnet, so that we can parse the String.
- Prints out the filtered String if logging s enabled.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#receive(java.lang.String)">receive</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>String</CODE> - s the String to be parsed<DT><B>Returns:</B><DD>a filtered String with no BSX sequences in it</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="addNotify()"><!-- --></A><H3>
-addNotify</H3>
-<PRE>
-public void <B>addNotify</B>()</PRE>
-<DL>
-<DD>Adds two Buttons to the Panel and instanciates a BSXWindow.<DD><DL>
-<DT><B>Overrides:</B><DD>addNotify in class java.awt.Panel</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;e)</PRE>
-<DL>
-<DD>Java 1.0 eventhandling routines.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>Event</CODE> - e<DT><B>Returns:</B><DD>true if Event has been recognized and fullfilled.<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="main(java.lang.String[])"><!-- --></A><H3>
-main</H3>
-<PRE>
-public static void <B>main</B>(java.lang.String[]&nbsp;args)</PRE>
-<DL>
-<DD>a main for test-purposes<DD><DL>
-</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="../modules/ButtonBar.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="BSXModule.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/ButtonBar.html b/web/root/telnet/Documentation/Source/modules/ButtonBar.html
deleted file mode 100644
index 9297f5368f..0000000000
--- a/web/root/telnet/Documentation/Source/modules/ButtonBar.html
+++ /dev/null
@@ -1,636 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Class  ButtonBar
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/BSXModule.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../modules/MudConnector.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="ButtonBar.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-modules</FONT>
-<BR>
-Class  ButtonBar</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<B>modules.ButtonBar</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>ButtonBar</B><DT>extends java.awt.Panel<DT>implements <A HREF="../modules/Module.html">Module</A></DL>
-
-<P>
-This class implements a programmable button bar.
- You can add <A HREF="#buttons">Buttons</A> and <A HREF="#fields">Input 
- fields</A> to trigger actions in the <A HREF="telnet.html">telnet 
- applet</A>. On how to load a module, please refer to the 
- <A HREF="telnet.html">telnet</A> documentation.
- 
- <DL>
-  <A NAME="buttons"></A>
-  <DT><B>Buttons:</B>
-  <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Button VALUE=&quot;<B><I>buttontext</I></B>|<B><I>buttonaction</I></B>&quot;&gt;</TT>
-  <DD><B><I>number</I></B> is the sequence number and determines the place
-      of the button on the row.
-      <P>
-  <DD><B><I>buttontext</I></B> is a string displayed on the button.
-      <P>
-  <DD><A NAME="buttonaction"><B><I>buttonaction</I></B></A> may be one
-      of the following functions or strings<BR>
-      <FONT SIZE=-1>(<I>Note:</I> the backslash character
-      in front of the dollar sign is mandatory!)</FONT>
-      <UL>
-       <LI><TT><I>simple text</I></TT>
-           to be sent to the remote host. Newline and/or carriage return
-           characters may be added in C syntax <B>\n</B> and <B>\r</B>.
-           To support unimplemented function keys the <B>\e</B> escape
-           character may be useful. The <B>\b</B> backspace character is
-           also supported.
-           The text may contain <A HREF="#fieldreference"><B><I>field 
-           reference(s)</I></B></A>.
-           <P>
-       <LI><TT>\$connect(<B><I>host</I></B>[,<B><I>port</I></B>])</TT> 
-           tries to initiate a connection to the <B><I>host</I></B>
-           at the <B><I>port</I></B>, if given. The standard port is
-           23. <B><I>host</I></B> and <B><I>port</I></B> may be hostname
-           and number or <A HREF="#fieldreference"><B><I>field
-           reference(s)</I></B></A>. If a connection already exists
-           nothing will happen.<BR>
-           <FONT SIZE=-1>(<I>Note:</I> It is not allowed to have
-           spaces anywhere inside the parenthesis!)</FONT>
-           <P>
-       <LI><TT>\$disconnect()</TT>
-           terminates the current connection, but if there was no
-           connection nothing will happen.
-           <P>
-       <LI><TT>\$detach()</TT>
-           detaches the applet from the web browser window and
-           creates a new frame externally. This may be used to allow
-           users to use the applet while browsing the web with the
-           same browser window.<BR>
-           <FONT SIZE=-1>(<I>Note:</I> You need to load the applet via the
-           <A HREF="appWrapper.html">appWrapper class</A> or
-           it will not work properly!)</FONT>
-      </UL>
-  <DD><B>Examples:</B><BR>
-      <FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
-      examples for input fields below.)</FONT>
-      <PRE>
-        &lt;PARAM NAME=1#Button VALUE="HELP!|help\r\n"&gt;
-        &lt;PARAM NAME=2#Button VALUE="HELP:|help \@help@\r\n"&gt;
-        &lt;PARAM NAME=4#Button VALUE="simple|\$connect(localhost)"&gt;
-        &lt;PARAM NAME=5#Button VALUE="complete|\$connect(www,4711)"&gt;
-        &lt;PARAM NAME=6#Button VALUE="connect|\$connect(\@address@)"&gt;
-        &lt;PARAM NAME=8#Button VALUE="connect to port|\$connect(\@address@,\@port@)"&gt;
-        &lt;PARAM NAME=10#Button VALUE="window|\$detach()"&gt;
-     </PRE>
-     <P>
-  <A NAME="fields"></A>
-  <DT><B>Input fields</B>
-  <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Input VALUE=&quot;<B><I>fieldname</I></B>[#<I><B>length</B></I>]|<B><I>initial text</I></B>[|<B><I>action</I></B>]&quot;&gt;</TT>
-  <DD><B><I>number</I></B> is the sequence number and determines the place
-      of the field on the row.
-      <P>
-  <DD><A NAME="fieldreference"><B><I>fieldname</I></B></A> is a
-      symbolic name to reference the input field. A reference may be used in 
-      <A HREF="#buttonaction"><B><I>button actions</I></B></A> and
-      is constructed as follows:
-      <TT>\@<B><I>fieldname</I></B>@</TT>
-      The <B>\@fieldname@</B> macro will be replaced by the string entered in
-      the text field.
-      <P>
-  <DD><B><I>length</I></B> is the length of the input field in numbers of
-      characters.
-      <P>
-  <DD><B><I>initial text</I></B> is the text to be placed into the input
-      field on startup
-  <DD><B><I>action</I></B> may be used similar to a 
-      <A HREF="#buttonaction"><B><I>button action</I></B></A>. This action 
-      will be used if the users presses Return in the inputfield. Leave
-      empty if you only want to use a button to send the text!
-  <DD><B>Examples:</B><BR>
-      <FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
-      examples for buttons before.)</FONT>
-      <PRE>
-        &lt;PARAM NAME=3#Input VALUE="help#10|"&gt;
-        &lt;PARAM NAME=7#Input VALUE="address|www.first.gmd.de"&gt;
-        &lt;PARAM NAME=8#Input VALUE="send#5|who|\@send@\r\n"&gt;
-        &lt;PARAM NAME=9#Input VALUE="port#5|4711"&gt;
-      </PRE>
-      <P>
- </DL>
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: ButtonBar.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../modules/Module.html"><CODE>Module</CODE></A>, <A HREF="../serialized-form.html#modules.ButtonBar">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#ButtonBar()">ButtonBar</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#addNotify()">addNotify</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;create the buttonbar from the parameter list.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;If the applet connects this method is called.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get notified of disconnection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;evt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#receive(java.lang.String)">receive</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This module does not take any input.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/ButtonBar.html#setLoader(java.lang.Object)">setLoader</A></B>(java.lang.Object&nbsp;o)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called by our loader to notify us of it.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="ButtonBar()"><!-- --></A><H3>
-ButtonBar</H3>
-<PRE>
-public <B>ButtonBar</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="setLoader(java.lang.Object)"><!-- --></A><H3>
-setLoader</H3>
-<PRE>
-public void <B>setLoader</B>(java.lang.Object&nbsp;o)</PRE>
-<DL>
-<DD>This method is called by our loader to notify us of it.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#setLoader(java.lang.Object)">setLoader</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>o</CODE> - The object that has loaded this object.<DT><B>See Also: </B><DD><CODE>display.Module</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;host,
-                    int&nbsp;port)</PRE>
-<DL>
-<DD>If the applet connects this method is called.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#connect(java.lang.String, int)">connect</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>host</CODE> - remote hostaddress - not used<DD><CODE>port</CODE> - remote port - not used</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()</PRE>
-<DL>
-<DD>Get notified of disconnection. Do nothing.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#disconnect()">disconnect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receive(java.lang.String)"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>This module does not take any input. It works passive.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#receive(java.lang.String)">receive</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Returns:</B><DD>null to remove from the list of receiver modules.<DT><B>See Also: </B><DD><CODE>display.Module</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="addNotify()"><!-- --></A><H3>
-addNotify</H3>
-<PRE>
-public void <B>addNotify</B>()</PRE>
-<DL>
-<DD>create the buttonbar from the parameter list. We will know our parent,
- when we have been added.<DD><DL>
-<DT><B>Overrides:</B><DD>addNotify in class java.awt.Panel</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;evt)</PRE>
-<DL>
-<DD><DL>
-<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/BSXModule.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../modules/MudConnector.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="ButtonBar.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/Module.html b/web/root/telnet/Documentation/Source/modules/Module.html
deleted file mode 100644
index e44b819ca8..0000000000
--- a/web/root/telnet/Documentation/Source/modules/Module.html
+++ /dev/null
@@ -1,235 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Interface  Module
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="Module.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-modules</FONT>
-<BR>
-Interface  Module</H2>
-<DL>
-<DT><B>All Known Implementing Classes:</B> <DD><A HREF="../modules/BSXModule.html">BSXModule</A>, <A HREF="../modules/TextLabel.html">TextLabel</A>, <A HREF="../modules/MudConnector.html">MudConnector</A>, <A HREF="../modules/Script.html">Script</A>, <A HREF="../modules/ButtonBar.html">ButtonBar</A></DD>
-</DL>
-<HR>
-<DL>
-<DT>public abstract interface <B>Module</B></DL>
-
-<P>
-Modules must implement this interface to be detected as valid modules
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: Module.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-</DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Module.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connected to the remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Module.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Disconnect from the host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Module.html#receive(java.lang.String)">receive</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Receive data from somewhere.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Module.html#setLoader(java.lang.Object)">setLoader</A></B>(java.lang.Object&nbsp;loader)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the loader of the module.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="setLoader(java.lang.Object)"><!-- --></A><H3>
-setLoader</H3>
-<PRE>
-public void <B>setLoader</B>(java.lang.Object&nbsp;loader)</PRE>
-<DL>
-<DD>Set the loader of the module. This is necessary to know if you want to
- contact the modules parent.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>loader</CODE> - The object that has loaded this module.</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;host,
-                    int&nbsp;port)</PRE>
-<DL>
-<DD>Connected to the remote host. This method notifies upon new connection.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>host</CODE> - remote hostname<DD><CODE>port</CODE> - remote port</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()</PRE>
-<DL>
-<DD>Disconnect from the host. This method notifies of lost connection.</DL>
-<HR>
-
-<A NAME="receive(java.lang.String)"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Receive data from somewhere. If a modules does not want to receive data
- it should return null to remove itself from the list of receiver modules.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>s</CODE> - The string we receive.<DT><B>Returns:</B><DD>the modified string or null (to remove from receiver list)</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="Module.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/MudConnector.html b/web/root/telnet/Documentation/Source/modules/MudConnector.html
deleted file mode 100644
index 3183a4f052..0000000000
--- a/web/root/telnet/Documentation/Source/modules/MudConnector.html
+++ /dev/null
@@ -1,571 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Class  MudConnector
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/ButtonBar.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../modules/Script.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="MudConnector.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-modules</FONT>
-<BR>
-Class  MudConnector</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<B>modules.MudConnector</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>MudConnector</B><DT>extends java.awt.Panel<DT>implements <A HREF="../modules/Module.html">Module</A>, java.lang.Runnable</DL>
-
-<P>
-A specially designed module for the 
- <A HREF="http://www.mudconnect.com/">MUD Connector</A>.
- It loads a list of MUDs via http and displays them in a list box to 
- be selected. A selected MUD can then be entered.
-
- <DL>
-  <DT><B>MudConnector parameterfile:</B>
-   <DD><PRE>&lt;PARAM NAME=MUDlist VALUE=&quot;<B><I>url</I></B>&quot;&gt;</PRE>
-   <DD>The url of the Mudlist. This url MUST be located on the same web
-       server as the applet!
- </DL>
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: MudConnector.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../modules/Module.html"><CODE>Module</CODE></A>, <A HREF="../serialized-form.html#modules.MudConnector">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../modules/MudConnector.html#MudConnector()">MudConnector</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#addNotify()">addNotify</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when newly added try to load the mudlist using the parameter "mudlist"</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dummy methods</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#handleEvent(java.awt.Event)">handleEvent</A></B>(java.awt.Event&nbsp;evt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handle list selection, connect, disconnect and refresh button</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#receive(java.lang.String)">receive</A></B>(java.lang.String&nbsp;str)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#run()">run</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The body of the thread opens a URLConnection with the address given as
- parameter "mudlist" and downloads it.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/MudConnector.html#setLoader(java.lang.Object)">setLoader</A></B>(java.lang.Object&nbsp;loader)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setLoader() is called upon start of the parent applet.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="MudConnector()"><!-- --></A><H3>
-MudConnector</H3>
-<PRE>
-public <B>MudConnector</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="setLoader(java.lang.Object)"><!-- --></A><H3>
-setLoader</H3>
-<PRE>
-public void <B>setLoader</B>(java.lang.Object&nbsp;loader)</PRE>
-<DL>
-<DD>setLoader() is called upon start of the parent applet. This method
- initializes the GUI of the module, e.g the list and buttons.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#setLoader(java.lang.Object)">setLoader</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>loader</CODE> - the parent applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="addNotify()"><!-- --></A><H3>
-addNotify</H3>
-<PRE>
-public void <B>addNotify</B>()</PRE>
-<DL>
-<DD>when newly added try to load the mudlist using the parameter "mudlist"<DD><DL>
-<DT><B>Overrides:</B><DD>addNotify in class java.awt.Panel</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="run()"><!-- --></A><H3>
-run</H3>
-<PRE>
-public void <B>run</B>()</PRE>
-<DL>
-<DD>The body of the thread opens a URLConnection with the address given as
- parameter "mudlist" and downloads it. It expects a tabulator separated
- list <mudname> <mudhost> <mudport> and the amount of muds in the file
- at the beginning of the file.<DD><DL>
-<DT><B>Specified by: </B><DD>run in interface java.lang.Runnable</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="handleEvent(java.awt.Event)"><!-- --></A><H3>
-handleEvent</H3>
-<PRE>
-public boolean <B>handleEvent</B>(java.awt.Event&nbsp;evt)</PRE>
-<DL>
-<DD>handle list selection, connect, disconnect and refresh button<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>evt</CODE> - the event to process<DT><B>Overrides:</B><DD>handleEvent in class java.awt.Component</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;host,
-                    int&nbsp;port)</PRE>
-<DL>
-<DD>dummy methods<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#connect(java.lang.String, int)">connect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()</PRE>
-<DL>
-<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#disconnect()">disconnect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receive(java.lang.String)"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>(java.lang.String&nbsp;str)</PRE>
-<DL>
-<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#receive(java.lang.String)">receive</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/ButtonBar.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../modules/Script.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="MudConnector.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/Script.html b/web/root/telnet/Documentation/Source/modules/Script.html
deleted file mode 100644
index 0861dce893..0000000000
--- a/web/root/telnet/Documentation/Source/modules/Script.html
+++ /dev/null
@@ -1,348 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Class  Script
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/MudConnector.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../modules/TextLabel.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="Script.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-modules</FONT>
-<BR>
-Class  Script</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.util.Dictionary
-        |
-        +--java.util.Hashtable
-              |
-              +--<B>modules.Script</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>Script</B><DT>extends java.util.Hashtable<DT>implements <A HREF="../modules/Module.html">Module</A></DL>
-
-<P>
-A very simple scripting module. It takes pairs of pattern and text and
- sends the corresponding text when the pattern matches. Each pattern is
- only matched once per connected session.
-
- <DL>
-  <DT><B>Scripts:</B>
-   <DD><PRE>&lt;PARAM NAME=script VALUE=&quot;<B><I>pattern</I></B>|<B><I>text</I></B>|<B><I>...</I></B>&quot;&gt;</PRE>
-   <DD>A script contains of pairs of <I>pattern</I> and <I>text</I> strings.
-       If the pattern is matched against the output from the remote host,
-       the corresponding text will be sent. Each pattern will match only
-       <B>once</B> per session. A session is defined by connect and 
-       disconnect.<P>
-       Thus it is possible to program an autologin as follows:<BR>
-       <PRE><B>"login:|leo|Password:|mypassword|leo@www|ls"</B></PRE>
-       Newlines will be added automatically to the string sent! At the
-       moment the order of the pattern and text pairs is <I>not</I> relevant.
-       <P>
-       It is possible to prompt the user for input if a match occurs. If the
-       corresponding <I>text</I> is a string enclosed in braces ([] or {}) a
-       dialog window is opened with <I>text</I> as prompt. A special case 
-       is an empty prompt in which case the <I>pattern</I> will be shown as 
-       prompt. &quot;[Your name:]&quot; would open a dialog window with the
-       text &quot;Your name&quot; as prompt. Curly braces have a special
-       meaning; any user input will be shown as &quot;*&quot; which makes
-       it possible to program password prompts. Example: 
-       &quot;{Your password:}&quot;.<P>
-       A special match like: &quot;login:|[]&quot; can be used to open a
-       dialog and display &quot;login:&quot; as prompt. This works for
-       &quot;{}&quot; as well.
-       
- </DL>
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: Script.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../modules/Module.html"><CODE>Module</CODE></A>, <A HREF="../serialized-form.html#modules.Script">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../modules/Script.html#Script()">Script</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Script.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Configure the script module by reading the script PARAMeter.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Script.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get notified of disconnection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Script.html#receive(java.lang.String)">receive</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called when data is received.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/Script.html#setLoader(java.lang.Object)">setLoader</A></B>(java.lang.Object&nbsp;o)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the applet as module loader</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.util.Hashtable"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.util.Hashtable</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clear, 
-clone, 
-contains, 
-containsKey, 
-containsValue, 
-elements, 
-entrySet, 
-equals, 
-get, 
-hashCode, 
-isEmpty, 
-keys, 
-keySet, 
-put, 
-putAll, 
-rehash, 
-remove, 
-size, 
-toString, 
-values</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>finalize, 
-getClass, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="Script()"><!-- --></A><H3>
-Script</H3>
-<PRE>
-public <B>Script</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="setLoader(java.lang.Object)"><!-- --></A><H3>
-setLoader</H3>
-<PRE>
-public void <B>setLoader</B>(java.lang.Object&nbsp;o)</PRE>
-<DL>
-<DD>Set the applet as module loader<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#setLoader(java.lang.Object)">setLoader</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>o</CODE> - the object that is the applet (must be an Applet)<DT><B>See Also: </B><DD><CODE>module.Module</CODE>, 
-<CODE>Applet</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;host,
-                    int&nbsp;port)</PRE>
-<DL>
-<DD>Configure the script module by reading the script PARAMeter.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#connect(java.lang.String, int)">connect</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>host</CODE> - remote hostaddress - not used<DD><CODE>port</CODE> - remote port - not used</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()</PRE>
-<DL>
-<DD>Get notified of disconnection. Do nothing.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#disconnect()">disconnect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receive(java.lang.String)"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>This method is called when data is received. It tries to match the
- input to the list of patterns and sends corresponding text on success.
- If the response is [] or {} the user will be prompted with the matching
- text. You can modify the prompt string by entering it inside of the
- brackets or curly braces (e.g. [Enter your id:]). In case of curly
- braces the input area will not show the typed in text (for passwords)!<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#receive(java.lang.String)">receive</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>s</CODE> - The string to test.<DT><B>See Also: </B><DD><CODE>peer.InputPeer</CODE></DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/MudConnector.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="../modules/TextLabel.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="Script.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/TextLabel.html b/web/root/telnet/Documentation/Source/modules/TextLabel.html
deleted file mode 100644
index 0c47df1748..0000000000
--- a/web/root/telnet/Documentation/Source/modules/TextLabel.html
+++ /dev/null
@@ -1,523 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Class  TextLabel
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/Script.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TextLabel.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-modules</FONT>
-<BR>
-Class  TextLabel</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--<B>modules.TextLabel</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>TextLabel</B><DT>extends java.awt.Panel<DT>implements <A HREF="../modules/Module.html">Module</A></DL>
-
-<P>
-This small module lets you display text somewhere in the applets area.
-
- <DL>
-  <DT><B>Label:</B>
-   <DD><PRE>&lt;PARAM NAME=labelRows    VALUE=&quot;<B><I>rows</B></I>&quot;&gt;</PRE>
-   <DD>Defines the how many rows the label will have.
-   <DD><PRE>&lt;PARAM NAME=labelCols    VALUE=&quot;<B><I>cols</B></I>&quot;&gt;</PRE>
-   <DD>Defines the how many columns the label will have.
-   <DD><PRE>&lt;PARAM NAME=labelFont    VALUE=&quot;<B><I>font[,size]</B></I>&quot;&gt;</PRE>
-   <DD>The font for displaying the label text. If the <I>size</I> is left out
-       a standard size of 14 points is assumed.
-   <DD><PRE>&lt;PARAM NAME=label#<I>number</I> VALUE=&quot;<B><I>text</I></B>&quot;&gt;</PRE>
-   <DT>The labels are enumerated and displayed in rows and columns.
- </DL>
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: TextLabel.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="../modules/Module.html"><CODE>Module</CODE></A>, <A HREF="../serialized-form.html#modules.TextLabel">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../modules/TextLabel.html#TextLabel()">TextLabel</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/TextLabel.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Do nothing upon connect.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/TextLabel.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Do nothing upon disconnecton.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/TextLabel.html#receive(java.lang.String)">receive</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Do nothing when receiving text.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../modules/TextLabel.html#setLoader(java.lang.Object)">setLoader</A></B>(java.lang.Object&nbsp;o)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the applet as module loader and configure.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Panel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Panel</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>addNotify</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocale, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-handleEvent, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-resize, 
-resize, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="TextLabel()"><!-- --></A><H3>
-TextLabel</H3>
-<PRE>
-public <B>TextLabel</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="setLoader(java.lang.Object)"><!-- --></A><H3>
-setLoader</H3>
-<PRE>
-public void <B>setLoader</B>(java.lang.Object&nbsp;o)</PRE>
-<DL>
-<DD>Set the applet as module loader and configure.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#setLoader(java.lang.Object)">setLoader</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>o</CODE> - the object that is the applet (must be an Applet)<DT><B>See Also: </B><DD><CODE>module.Module</CODE>, 
-<CODE>Applet</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;host,
-                    int&nbsp;port)</PRE>
-<DL>
-<DD>Do nothing upon connect.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#connect(java.lang.String, int)">connect</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>host</CODE> - remote hostaddress - not used<DD><CODE>port</CODE> - remote port - not used</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()</PRE>
-<DL>
-<DD>Do nothing upon disconnecton.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#disconnect()">disconnect</A> in interface <A HREF="../modules/Module.html">Module</A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receive(java.lang.String)"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>(java.lang.String&nbsp;s)</PRE>
-<DL>
-<DD>Do nothing when receiving text. Be removed upon first call.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../modules/Module.html#receive(java.lang.String)">receive</A> in interface <A HREF="../modules/Module.html">Module</A><DT><B>Parameters:</B><DD><CODE>s</CODE> - The string received.<DT><B>See Also: </B><DD><CODE>peer.InputPeer</CODE></DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/Script.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TextLabel.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_java.awt.Component">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/package-frame.html b/web/root/telnet/Documentation/Source/modules/package-frame.html
deleted file mode 100644
index dab1f3cd1d..0000000000
--- a/web/root/telnet/Documentation/Source/modules/package-frame.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Package modules
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-<FONT size="+1" ID="FrameTitleFont">
-<A HREF="../modules/package-summary.html" TARGET="classFrame">modules</A></FONT>
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Interfaces</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="Module.html" TARGET="classFrame"><I>Module</I></A></FONT></TD>
-</TR>
-</TABLE>
-
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Classes</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="BSXModule.html" TARGET="classFrame">BSXModule</A>
-<BR>
-<A HREF="ButtonBar.html" TARGET="classFrame">ButtonBar</A>
-<BR>
-<A HREF="MudConnector.html" TARGET="classFrame">MudConnector</A>
-<BR>
-<A HREF="Script.html" TARGET="classFrame">Script</A>
-<BR>
-<A HREF="TextLabel.html" TARGET="classFrame">TextLabel</A></FONT></TD>
-</TR>
-</TABLE>
-
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/package-summary.html b/web/root/telnet/Documentation/Source/modules/package-summary.html
deleted file mode 100644
index 6ff39f7c09..0000000000
--- a/web/root/telnet/Documentation/Source/modules/package-summary.html
+++ /dev/null
@@ -1,136 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Package modules
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/package-summary.html"><B>PREV PACKAGE</B></A>&nbsp;
-&nbsp;<A HREF="../socket/package-summary.html"><B>NEXT PACKAGE</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<H2>
-Package modules
-</H2>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Interface Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="Module.html"><I>Module</I></A></B></TD>
-<TD>Modules must implement this interface to be detected as valid modules</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="BSXModule.html">BSXModule</A></B></TD>
-<TD>The ultimate BSX module implements most of the common used
- BSX controll sequences.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="ButtonBar.html">ButtonBar</A></B></TD>
-<TD>This class implements a programmable button bar.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="MudConnector.html">MudConnector</A></B></TD>
-<TD>A specially designed module for the 
- <A HREF="http://www.mudconnect.com/">MUD Connector</A>.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="Script.html">Script</A></B></TD>
-<TD>A very simple scripting module.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="TextLabel.html">TextLabel</A></B></TD>
-<TD>This small module lets you display text somewhere in the applets area.</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/package-summary.html"><B>PREV PACKAGE</B></A>&nbsp;
-&nbsp;<A HREF="../socket/package-summary.html"><B>NEXT PACKAGE</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/modules/package-tree.html b/web/root/telnet/Documentation/Source/modules/package-tree.html
deleted file mode 100644
index 00d9e9dd6b..0000000000
--- a/web/root/telnet/Documentation/Source/modules/package-tree.html
+++ /dev/null
@@ -1,124 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: modules Class Hierarchy
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/package-tree.html"><B>PREV</B></A>&nbsp;
-&nbsp;<A HREF="../socket/package-tree.html"><B>NEXT</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H2>
-Hierarchy For Package modules
-</H2>
-</CENTER>
-<DL>
-<DT><B>Package Hierarchies: </B><DD><A HREF="../overview-tree.html">All Packages</A></DL>
-<HR>
-<H2>
-Class Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">class java.lang.Object<UL>
-<LI TYPE="circle">class java.awt.Component (implements java.awt.image.ImageObserver, java.awt.MenuContainer, java.io.Serializable)
-<UL>
-<LI TYPE="circle">class java.awt.Container<UL>
-<LI TYPE="circle">class java.awt.Panel<UL>
-<LI TYPE="circle">class modules.<A HREF="../modules/BSXModule.html"><B>BSXModule</B></A> (implements modules.<A HREF="../modules/Module.html">Module</A>)
-<LI TYPE="circle">class modules.<A HREF="../modules/ButtonBar.html"><B>ButtonBar</B></A> (implements modules.<A HREF="../modules/Module.html">Module</A>)
-<LI TYPE="circle">class modules.<A HREF="../modules/MudConnector.html"><B>MudConnector</B></A> (implements modules.<A HREF="../modules/Module.html">Module</A>, java.lang.Runnable)
-<LI TYPE="circle">class modules.<A HREF="../modules/TextLabel.html"><B>TextLabel</B></A> (implements modules.<A HREF="../modules/Module.html">Module</A>)
-</UL>
-</UL>
-</UL>
-<LI TYPE="circle">class java.util.Dictionary<UL>
-<LI TYPE="circle">class java.util.Hashtable (implements java.lang.Cloneable, java.util.Map, java.io.Serializable)
-<UL>
-<LI TYPE="circle">class modules.<A HREF="../modules/Script.html"><B>Script</B></A> (implements modules.<A HREF="../modules/Module.html">Module</A>)
-</UL>
-</UL>
-</UL>
-</UL>
-<H2>
-Interface Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">interface modules.<A HREF="../modules/Module.html"><B>Module</B></A></UL>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../display/package-tree.html"><B>PREV</B></A>&nbsp;
-&nbsp;<A HREF="../socket/package-tree.html"><B>NEXT</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/overview-frame.html b/web/root/telnet/Documentation/Source/overview-frame.html
deleted file mode 100644
index cc150b57d8..0000000000
--- a/web/root/telnet/Documentation/Source/overview-frame.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Overview
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameTitleFont">
-<B></B></FONT></TD>
-</TR>
-</TABLE>
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT ID="FrameItemFont"><A HREF="allclasses-frame.html" TARGET="packageFrame">All Classes</A></FONT>
-<P>
-<FONT size="+1" ID="FrameHeadingFont">
-Packages</FONT>
-<BR>
-<FONT ID="FrameItemFont"><A HREF="display/package-frame.html" TARGET="packageFrame">display</A></FONT>
-<BR>
-<FONT ID="FrameItemFont"><A HREF="modules/package-frame.html" TARGET="packageFrame">modules</A></FONT>
-<BR>
-<FONT ID="FrameItemFont"><A HREF="socket/package-frame.html" TARGET="packageFrame">socket</A></FONT>
-<BR>
-</TD>
-</TR>
-</TABLE>
-
-<P>
-&nbsp;
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/overview-summary.html b/web/root/telnet/Documentation/Source/overview-summary.html
deleted file mode 100644
index 6579b2f622..0000000000
--- a/web/root/telnet/Documentation/Source/overview-summary.html
+++ /dev/null
@@ -1,108 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Overview
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Overview</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="overview-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Packages</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="20%"><B><A HREF="display/package-summary.html">display</A></B></TD>
-<TD>&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="20%"><B><A HREF="modules/package-summary.html">modules</A></B></TD>
-<TD>&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="20%"><B><A HREF="socket/package-summary.html">socket</A></B></TD>
-<TD>&nbsp;</TD>
-</TR>
-</TABLE>
-
-<P>
-&nbsp;<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Overview</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="overview-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/overview-tree.html b/web/root/telnet/Documentation/Source/overview-tree.html
deleted file mode 100644
index 79d996ed79..0000000000
--- a/web/root/telnet/Documentation/Source/overview-tree.html
+++ /dev/null
@@ -1,139 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:30 CEST 1999 -->
-<TITLE>
-: Class Hierarchy
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H2>
-Hierarchy For All Packages</H2>
-</CENTER>
-<DL>
-<DT><B>Package Hierarchies: </B><DD><A HREF="display/package-tree.html">display</A>, <A HREF="modules/package-tree.html">modules</A>, <A HREF="socket/package-tree.html">socket</A></DL>
-<HR>
-<H2>
-Class Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">class java.lang.Object<UL>
-<LI TYPE="circle">class java.awt.Component (implements java.awt.image.ImageObserver, java.awt.MenuContainer, java.io.Serializable)
-<UL>
-<LI TYPE="circle">class java.awt.Container<UL>
-<LI TYPE="circle">class java.awt.Panel<UL>
-<LI TYPE="circle">class java.applet.Applet<UL>
-<LI TYPE="circle">class <A HREF="appWrapper.html"><B>appWrapper</B></A> (implements java.applet.AppletStub, java.lang.Runnable)
-<LI TYPE="circle">class <A HREF="CharDisplayTest.html"><B>CharDisplayTest</B></A><LI TYPE="circle">class <A HREF="telnet.html"><B>telnet</B></A> (implements java.lang.Runnable, socket.<A HREF="socket/StatusPeer.html">StatusPeer</A>, display.<A HREF="display/TerminalHost.html">TerminalHost</A>)
-</UL>
-<LI TYPE="circle">class modules.<A HREF="modules/BSXModule.html"><B>BSXModule</B></A> (implements modules.<A HREF="modules/Module.html">Module</A>)
-<LI TYPE="circle">class modules.<A HREF="modules/ButtonBar.html"><B>ButtonBar</B></A> (implements modules.<A HREF="modules/Module.html">Module</A>)
-<LI TYPE="circle">class display.<A HREF="display/CharDisplay.html"><B>CharDisplay</B></A><LI TYPE="circle">class modules.<A HREF="modules/MudConnector.html"><B>MudConnector</B></A> (implements modules.<A HREF="modules/Module.html">Module</A>, java.lang.Runnable)
-<LI TYPE="circle">class display.<A HREF="display/Terminal.html"><B>Terminal</B></A><UL>
-<LI TYPE="circle">class display.<A HREF="display/vt320.html"><B>vt320</B></A> (implements display.<A HREF="display/TerminalHost.html">TerminalHost</A>)
-</UL>
-<LI TYPE="circle">class modules.<A HREF="modules/TextLabel.html"><B>TextLabel</B></A> (implements modules.<A HREF="modules/Module.html">Module</A>)
-</UL>
-</UL>
-</UL>
-<LI TYPE="circle">class java.util.Dictionary<UL>
-<LI TYPE="circle">class java.util.Hashtable (implements java.lang.Cloneable, java.util.Map, java.io.Serializable)
-<UL>
-<LI TYPE="circle">class modules.<A HREF="modules/Script.html"><B>Script</B></A> (implements modules.<A HREF="modules/Module.html">Module</A>)
-</UL>
-</UL>
-<LI TYPE="circle">class <A HREF="proxy.html"><B>proxy</B></A> (implements java.lang.Runnable)
-<LI TYPE="circle">class display.<A HREF="display/SoftFont.html"><B>SoftFont</B></A><LI TYPE="circle">class socket.<A HREF="socket/TelnetIO.html"><B>TelnetIO</B></A> (implements socket.<A HREF="socket/StatusPeer.html">StatusPeer</A>)
-<LI TYPE="circle">class socket.<A HREF="socket/TelnetWrapper.html"><B>TelnetWrapper</B></A><LI TYPE="circle">class java.lang.Throwable (implements java.io.Serializable)
-<UL>
-<LI TYPE="circle">class java.lang.Exception<UL>
-<LI TYPE="circle">class java.io.IOException<UL>
-<LI TYPE="circle">class socket.<A HREF="socket/TimedOutException.html"><B>TimedOutException</B></A></UL>
-</UL>
-</UL>
-</UL>
-</UL>
-<H2>
-Interface Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">interface modules.<A HREF="modules/Module.html"><B>Module</B></A><LI TYPE="circle">interface socket.<A HREF="socket/StatusPeer.html"><B>StatusPeer</B></A><LI TYPE="circle">interface display.<A HREF="display/TerminalHost.html"><B>TerminalHost</B></A></UL>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/package-list b/web/root/telnet/Documentation/Source/package-list
deleted file mode 100644
index 1b8a62b2c9..0000000000
--- a/web/root/telnet/Documentation/Source/package-list
+++ /dev/null
@@ -1,3 +0,0 @@
-display
-modules
-socket
diff --git a/web/root/telnet/Documentation/Source/packages.html b/web/root/telnet/Documentation/Source/packages.html
deleted file mode 100644
index 4bca39c29c..0000000000
--- a/web/root/telnet/Documentation/Source/packages.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:31 CEST 1999 -->
-<TITLE>
-
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<BR>
-
-<BR>
-
-<BR>
-<CENTER>
-The front page has been renamed.Please see:
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="index.html">Frame version</A>
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="overview-summary.html">Non-frame version.</A></CENTER>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/proxy.html b/web/root/telnet/Documentation/Source/proxy.html
deleted file mode 100644
index f5e6778ca1..0000000000
--- a/web/root/telnet/Documentation/Source/proxy.html
+++ /dev/null
@@ -1,252 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:33 CEST 1999 -->
-<TITLE>
-: Class  proxy
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="CharDisplayTest.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="telnet.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="proxy.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-Class  proxy</H2>
-<PRE>
-java.lang.Object
-  |
-  +--<B>proxy</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>proxy</B><DT>extends java.lang.Object<DT>implements java.lang.Runnable</DL>
-
-<P>
-proxy class -- implements a proxy server to redirect network access
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: proxy.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-</DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="proxy.html#proxy(int, java.lang.String, int)">proxy</A></B>(int&nbsp;lport,
-      java.lang.String&nbsp;raddr,
-      int&nbsp;rport)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Create a server socket and start listening on the local port.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="proxy.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;args)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called when the application is run on the commandline.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="proxy.html#run()">run</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cycle around until an error occurs and wait for incoming connections.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-toString, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="proxy(int, java.lang.String, int)"><!-- --></A><H3>
-proxy</H3>
-<PRE>
-public <B>proxy</B>(int&nbsp;lport,
-             java.lang.String&nbsp;raddr,
-             int&nbsp;rport)</PRE>
-<DL>
-<DD>Create a server socket and start listening on the local port.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>lport</CODE> - local port<DD><CODE>raddr</CODE> - address of the destination<DD><CODE>rport</CODE> - port on the destination host</DL>
-</DD>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="main(java.lang.String[])"><!-- --></A><H3>
-main</H3>
-<PRE>
-public static void <B>main</B>(java.lang.String[]&nbsp;args)</PRE>
-<DL>
-<DD>This method is called when the application is run on the commandline.
- It takes two or three arguments:
- usage: java proxy local-port destination-host destination-port<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>args</CODE> - The command line arguments</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="run()"><!-- --></A><H3>
-run</H3>
-<PRE>
-public void <B>run</B>()</PRE>
-<DL>
-<DD>Cycle around until an error occurs and wait for incoming connections.
- An incoming connection will create two redirectors. One for
- local-host - destination-host and one for destination-host - local-host.<DD><DL>
-<DT><B>Specified by: </B><DD>run in interface java.lang.Runnable</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="CharDisplayTest.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;<A HREF="telnet.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="proxy.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/serialized-form.html b/web/root/telnet/Documentation/Source/serialized-form.html
deleted file mode 100644
index 72f77d6f63..0000000000
--- a/web/root/telnet/Documentation/Source/serialized-form.html
+++ /dev/null
@@ -1,1635 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:36 CEST 1999 -->
-<TITLE>
-Serialized Form
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H1>
-Serialized Form</H1>
-</CENTER>
-<HR SIZE="4" NOSHADE>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD ALIGN="center"><FONT SIZE="+2">
-<B>Package</B> <B>display</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="display.CharDisplay"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="display/CharDisplay.html">display.CharDisplay</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-bottomMargin</H3>
-<PRE>
-int <B>bottomMargin</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-bufSize</H3>
-<PRE>
-int <B>bufSize</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-charArray</H3>
-<PRE>
-char[][] <B>charArray</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-charAttributes</H3>
-<PRE>
-int[][] <B>charAttributes</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-charDescent</H3>
-<PRE>
-int <B>charDescent</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-charHeight</H3>
-<PRE>
-int <B>charHeight</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-charWidth</H3>
-<PRE>
-int <B>charWidth</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-color</H3>
-<PRE>
-java.awt.Color[] <B>color</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-cursorX</H3>
-<PRE>
-int <B>cursorX</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-cursorY</H3>
-<PRE>
-int <B>cursorY</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-fm</H3>
-<PRE>
-java.awt.FontMetrics <B>fm</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-insets</H3>
-<PRE>
-java.awt.Insets <B>insets</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-maxBufSize</H3>
-<PRE>
-int <B>maxBufSize</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-normalFont</H3>
-<PRE>
-java.awt.Font <B>normalFont</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-raised</H3>
-<PRE>
-boolean <B>raised</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-resizeStrategy</H3>
-<PRE>
-int <B>resizeStrategy</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-scrBarPos</H3>
-<PRE>
-java.lang.String <B>scrBarPos</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-screenBase</H3>
-<PRE>
-int <B>screenBase</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-screenLocked</H3>
-<PRE>
-boolean <B>screenLocked</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-scrollBar</H3>
-<PRE>
-java.awt.Scrollbar <B>scrollBar</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-selectBegin</H3>
-<PRE>
-java.awt.Point <B>selectBegin</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-selectEnd</H3>
-<PRE>
-java.awt.Point <B>selectEnd</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-selectFrame</H3>
-<PRE>
-java.awt.Frame <B>selectFrame</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-selection</H3>
-<PRE>
-java.awt.TextArea <B>selection</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-sf</H3>
-<PRE>
-<A HREF="display/SoftFont.html">SoftFont</A> <B>sf</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-size</H3>
-<PRE>
-java.awt.Dimension <B>size</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-topMargin</H3>
-<PRE>
-int <B>topMargin</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-update</H3>
-<PRE>
-boolean[] <B>update</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-version</H3>
-<PRE>
-java.lang.String <B>version</B></PRE>
-<DL>
-<DD>If you need the runtime version, just ask this variable.</DL>
-<HR>
-
-<H3>
-windowBase</H3>
-<PRE>
-int <B>windowBase</B></PRE>
-<DL>
-</DL>
-
-<P>
-<A NAME="display.Terminal"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="display/Terminal.html">display.Terminal</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-
-<P>
-<A NAME="display.vt320"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="display/vt320.html">display.vt320</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-attributes</H3>
-<PRE>
-int <B>attributes</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-C</H3>
-<PRE>
-int <B>C</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-DCEvar</H3>
-<PRE>
-int <B>DCEvar</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-DCEvars</H3>
-<PRE>
-int[] <B>DCEvars</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-dcs</H3>
-<PRE>
-java.lang.String <B>dcs</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-display</H3>
-<PRE>
-<A HREF="display/CharDisplay.html">CharDisplay</A> <B>display</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Do</H3>
-<PRE>
-java.lang.String <B>Do</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Find</H3>
-<PRE>
-java.lang.String <B>Find</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-FunctionKey</H3>
-<PRE>
-java.lang.String[] <B>FunctionKey</B></PRE>
-<DL>
-<DD>Strings to send on function key presseic</DL>
-<HR>
-
-<H3>
-FunctionKeyAlt</H3>
-<PRE>
-java.lang.String[] <B>FunctionKeyAlt</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-FunctionKeyCtrl</H3>
-<PRE>
-java.lang.String[] <B>FunctionKeyCtrl</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-FunctionKeyShift</H3>
-<PRE>
-java.lang.String[] <B>FunctionKeyShift</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Help</H3>
-<PRE>
-java.lang.String <B>Help</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-host</H3>
-<PRE>
-<A HREF="display/TerminalHost.html">TerminalHost</A> <B>host</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Insert</H3>
-<PRE>
-java.lang.String <B>Insert</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-insertmode</H3>
-<PRE>
-int <B>insertmode</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KeyBacktab</H3>
-<PRE>
-java.lang.String <B>KeyBacktab</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KeyDown</H3>
-<PRE>
-java.lang.String <B>KeyDown</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KeyLeft</H3>
-<PRE>
-java.lang.String <B>KeyLeft</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KeyRight</H3>
-<PRE>
-java.lang.String <B>KeyRight</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KeyTab</H3>
-<PRE>
-java.lang.String <B>KeyTab</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KeyUp</H3>
-<PRE>
-java.lang.String <B>KeyUp</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP0</H3>
-<PRE>
-java.lang.String <B>KP0</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP1</H3>
-<PRE>
-java.lang.String <B>KP1</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP2</H3>
-<PRE>
-java.lang.String <B>KP2</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP3</H3>
-<PRE>
-java.lang.String <B>KP3</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP4</H3>
-<PRE>
-java.lang.String <B>KP4</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP5</H3>
-<PRE>
-java.lang.String <B>KP5</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP6</H3>
-<PRE>
-java.lang.String <B>KP6</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP7</H3>
-<PRE>
-java.lang.String <B>KP7</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP8</H3>
-<PRE>
-java.lang.String <B>KP8</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KP9</H3>
-<PRE>
-java.lang.String <B>KP9</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KPComma</H3>
-<PRE>
-java.lang.String <B>KPComma</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KPEnter</H3>
-<PRE>
-java.lang.String <B>KPEnter</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KPMinus</H3>
-<PRE>
-java.lang.String <B>KPMinus</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-KPPeriod</H3>
-<PRE>
-java.lang.String <B>KPPeriod</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-moveoutsidemargins</H3>
-<PRE>
-boolean <B>moveoutsidemargins</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-NextScn</H3>
-<PRE>
-java.lang.String <B>NextScn</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-normalcursor</H3>
-<PRE>
-int <B>normalcursor</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-osc</H3>
-<PRE>
-java.lang.String <B>osc</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-osn</H3>
-<PRE>
-java.lang.String <B>osn</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-PF1</H3>
-<PRE>
-java.lang.String <B>PF1</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-PF2</H3>
-<PRE>
-java.lang.String <B>PF2</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-PF3</H3>
-<PRE>
-java.lang.String <B>PF3</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-PF4</H3>
-<PRE>
-java.lang.String <B>PF4</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-pressedKey</H3>
-<PRE>
-int <B>pressedKey</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-pressedWhen</H3>
-<PRE>
-long <B>pressedWhen</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-PrevScn</H3>
-<PRE>
-java.lang.String <B>PrevScn</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-R</H3>
-<PRE>
-int <B>R</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Remove</H3>
-<PRE>
-java.lang.String <B>Remove</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Sa</H3>
-<PRE>
-int <B>Sa</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Sc</H3>
-<PRE>
-int <B>Sc</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Select</H3>
-<PRE>
-java.lang.String <B>Select</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-sendcrlf</H3>
-<PRE>
-boolean <B>sendcrlf</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Sr</H3>
-<PRE>
-int <B>Sr</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-statusmode</H3>
-<PRE>
-int <B>statusmode</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-Tabs</H3>
-<PRE>
-byte[] <B>Tabs</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-term_state</H3>
-<PRE>
-int <B>term_state</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-terminalID</H3>
-<PRE>
-java.lang.String <B>terminalID</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-useibmcharset</H3>
-<PRE>
-boolean <B>useibmcharset</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-vms</H3>
-<PRE>
-boolean <B>vms</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-vt52mode</H3>
-<PRE>
-int <B>vt52mode</B></PRE>
-<DL>
-</DL>
-
-<P>
-<HR SIZE="4" NOSHADE>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD ALIGN="center"><FONT SIZE="+2">
-<B>Package</B> <B>modules</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="modules.BSXModule"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="modules/BSXModule.html">modules.BSXModule</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-bsxButton</H3>
-<PRE>
-java.awt.Button <B>bsxButton</B></PRE>
-<DL>
-<DD>the Buttons to switch on and off the logging and BSX handling</DL>
-<HR>
-
-<H3>
-bsxCheckIt</H3>
-<PRE>
-boolean <B>bsxCheckIt</B></PRE>
-<DL>
-<DD>the states of the Buttons</DL>
-<HR>
-
-<H3>
-BSXData</H3>
-<PRE>
-java.lang.String <B>BSXData</B></PRE>
-<DL>
-<DD>holds the BSX description, including the numbers of polygons and edges</DL>
-<HR>
-
-<H3>
-bsxWindow</H3>
-<PRE>
-modules.bsx.BSXDisplay <B>bsxWindow</B></PRE>
-<DL>
-<DD>The Frame, where the graphic is drawn</DL>
-<HR>
-
-<H3>
-clientVersion</H3>
-<PRE>
-java.lang.String <B>clientVersion</B></PRE>
-<DL>
-<DD>Client Version</DL>
-<HR>
-
-<H3>
-intEdges</H3>
-<PRE>
-int <B>intEdges</B></PRE>
-<DL>
-<DD>holds the number of edges in the current polygon as an int</DL>
-<HR>
-
-<H3>
-intPolys</H3>
-<PRE>
-int <B>intPolys</B></PRE>
-<DL>
-<DD>holds the number of polygons in the current description as an int</DL>
-<HR>
-
-<H3>
-intXPos</H3>
-<PRE>
-int <B>intXPos</B></PRE>
-<DL>
-<DD>holds the X position of the current object as an int</DL>
-<HR>
-
-<H3>
-intYPos</H3>
-<PRE>
-int <B>intYPos</B></PRE>
-<DL>
-<DD>holds the Y position of the current object as an int</DL>
-<HR>
-
-<H3>
-lastCommand</H3>
-<PRE>
-java.lang.String <B>lastCommand</B></PRE>
-<DL>
-<DD>lastCommand is a Statedescriptor.<BR>
- if lastCommand.length()=4 then the entire Command has been read
- else go on reading the command completly<BR>
- Commands are: PRO SCE VIO DFS DFO TMS RMO RFS RQV PUR
- additional commands are: BOM EOM LON LOF TXT which will not be supported 
- by Regenbogen BSX java Client.<BR>
- Commands are preceeded by a @</DL>
-<HR>
-
-<H3>
-lastState</H3>
-<PRE>
-int <B>lastState</B></PRE>
-<DL>
-<DD>lastState identifies what we have done last time we received a string
- <UL>
- <LI>-1 undefined -> used to mark succesful work
- <LI> 0 nothing -> go on searching for a new command
- <LI> 1 identifier -> we have to complete the identifier
- <LI> 2 download -> we have to get more BSXData
- </UL></DL>
-<HR>
-
-<H3>
-logButton</H3>
-<PRE>
-java.awt.Button <B>logButton</B></PRE>
-<DL>
-<DD>the Buttons to switch on and off the logging and BSX handling</DL>
-<HR>
-
-<H3>
-logging</H3>
-<PRE>
-boolean <B>logging</B></PRE>
-<DL>
-<DD>the states of the Buttons</DL>
-<HR>
-
-<H3>
-parent</H3>
-<PRE>
-<A HREF="telnet.html">telnet</A> <B>parent</B></PRE>
-<DL>
-<DD>Our parent, the telnet app.</DL>
-<HR>
-
-<H3>
-strEdges</H3>
-<PRE>
-java.lang.String <B>strEdges</B></PRE>
-<DL>
-<DD>holds the number of edges in the current polygon as a String</DL>
-<HR>
-
-<H3>
-strIdentifier</H3>
-<PRE>
-java.lang.String <B>strIdentifier</B></PRE>
-<DL>
-<DD>holds the identifier of the current object</DL>
-<HR>
-
-<H3>
-strPolys</H3>
-<PRE>
-java.lang.String <B>strPolys</B></PRE>
-<DL>
-<DD>holds the number of polygons in the current description as a String</DL>
-<HR>
-
-<H3>
-strXPos</H3>
-<PRE>
-java.lang.String <B>strXPos</B></PRE>
-<DL>
-<DD>holds the X position of the current object as a String</DL>
-<HR>
-
-<H3>
-strYPos</H3>
-<PRE>
-java.lang.String <B>strYPos</B></PRE>
-<DL>
-<DD>holds the Y position of the current object as a String</DL>
-<HR>
-
-<H3>
-toplevel</H3>
-<PRE>
-java.awt.Container <B>toplevel</B></PRE>
-<DL>
-<DD>the Container, wich is around us</DL>
-
-<P>
-<A NAME="modules.ButtonBar"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="modules/ButtonBar.html">modules.ButtonBar</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-buttons</H3>
-<PRE>
-java.util.Hashtable <B>buttons</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-fields</H3>
-<PRE>
-java.util.Hashtable <B>fields</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-parent</H3>
-<PRE>
-<A HREF="telnet.html">telnet</A> <B>parent</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-toplevel</H3>
-<PRE>
-java.awt.Container <B>toplevel</B></PRE>
-<DL>
-</DL>
-
-<P>
-<A NAME="modules.MudConnector"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="modules/MudConnector.html">modules.MudConnector</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-address</H3>
-<PRE>
-java.awt.Panel <B>address</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-app</H3>
-<PRE>
-<A HREF="telnet.html">telnet</A> <B>app</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-connectButton</H3>
-<PRE>
-java.awt.Button <B>connectButton</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-disconnectButton</H3>
-<PRE>
-java.awt.Button <B>disconnectButton</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-display</H3>
-<PRE>
-java.awt.List <B>display</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-index</H3>
-<PRE>
-java.util.Vector <B>index</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-indicator</H3>
-<PRE>
-java.awt.Label <B>indicator</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-info</H3>
-<PRE>
-java.awt.TextField <B>info</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-layouter</H3>
-<PRE>
-java.awt.CardLayout <B>layouter</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-progress</H3>
-<PRE>
-java.awt.Panel <B>progress</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-refreshButton</H3>
-<PRE>
-java.awt.Button <B>refreshButton</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-server</H3>
-<PRE>
-java.net.URL <B>server</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-showButton</H3>
-<PRE>
-java.awt.Button <B>showButton</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-url</H3>
-<PRE>
-java.lang.String <B>url</B></PRE>
-<DL>
-</DL>
-
-<P>
-<A NAME="modules.Script"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="modules/Script.html">modules.Script</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-applet</H3>
-<PRE>
-<A HREF="telnet.html">telnet</A> <B>applet</B></PRE>
-<DL>
-</DL>
-
-<P>
-<A NAME="modules.TextLabel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="modules/TextLabel.html">modules.TextLabel</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-applet</H3>
-<PRE>
-<A HREF="telnet.html">telnet</A> <B>applet</B></PRE>
-<DL>
-</DL>
-
-<P>
-<HR SIZE="4" NOSHADE>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD ALIGN="center"><FONT SIZE="+2">
-<B>Package</B> <B>socket</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="socket.TimedOutException"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="socket/TimedOutException.html">socket.TimedOutException</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-
-<P>
-<HR SIZE="4" NOSHADE>
-<A NAME="appWrapper"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="appWrapper.html">appWrapper</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-applet</H3>
-<PRE>
-java.applet.Applet <B>applet</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-appletName</H3>
-<PRE>
-java.lang.String <B>appletName</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-f</H3>
-<PRE>
-frame <B>f</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-frameTitle</H3>
-<PRE>
-java.lang.String <B>frameTitle</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-loader</H3>
-<PRE>
-java.lang.Thread <B>loader</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-startButton</H3>
-<PRE>
-java.awt.Button <B>startButton</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-startLabel</H3>
-<PRE>
-java.lang.String <B>startLabel</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-stopLabel</H3>
-<PRE>
-java.lang.String <B>stopLabel</B></PRE>
-<DL>
-</DL>
-
-<P>
-<HR SIZE="4" NOSHADE>
-<A NAME="CharDisplayTest"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="CharDisplayTest.html">CharDisplayTest</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-attr</H3>
-<PRE>
-java.awt.Button <B>attr</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-buttons</H3>
-<PRE>
-java.awt.Panel <B>buttons</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-chars</H3>
-<PRE>
-java.awt.Button <B>chars</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-display</H3>
-<PRE>
-<A HREF="display/CharDisplay.html">CharDisplay</A> <B>display</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-fonts</H3>
-<PRE>
-java.awt.Choice <B>fonts</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-from</H3>
-<PRE>
-java.awt.TextField <B>from</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-info</H3>
-<PRE>
-java.awt.Button <B>info</B></PRE>
-<DL>
-</DL>
-
-<P>
-<HR SIZE="4" NOSHADE>
-<A NAME="telnet"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableSubHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class <A HREF="telnet.html">telnet</A> implements Serializable</B></FONT></TD>
-</TR>
-</TABLE>
-
-<P>
-<A NAME="serializedForm"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Serialized Fields</B></FONT></TD>
-</TR>
-</TABLE>
-
-<H3>
-address</H3>
-<PRE>
-java.lang.String <B>address</B></PRE>
-<DL>
-<DD>The host address to connect to. This is retrieved from the PARAM tag
- "address".</DL>
-<HR>
-
-<H3>
-connected</H3>
-<PRE>
-boolean <B>connected</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-emulation</H3>
-<PRE>
-java.lang.String <B>emulation</B></PRE>
-<DL>
-<DD>Emulation type (default is vt320). This can be specified as the PARAM
- tag "emulation".<DD><DL>
-<DT><B>See Also: </B><DD><CODE>term</CODE>, 
-<A HREF="display/Terminal.html"><CODE>Terminal</CODE></A>, 
-<A HREF="display/TerminalHost.html"><CODE>TerminalHost</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<H3>
-localecho</H3>
-<PRE>
-boolean <B>localecho</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-modules</H3>
-<PRE>
-java.util.Vector <B>modules</B></PRE>
-<DL>
-<DD>Dynamically loaded modules are stored here.</DL>
-<HR>
-
-<H3>
-params</H3>
-<PRE>
-java.util.Hashtable <B>params</B></PRE>
-<DL>
-<DD>This Hashtable contains information retrievable by getParameter() in case
- the program is run as an application and the AppletStub is missing.</DL>
-<HR>
-
-<H3>
-parent</H3>
-<PRE>
-java.awt.Container <B>parent</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-port</H3>
-<PRE>
-int <B>port</B></PRE>
-<DL>
-<DD>The port number (default ist 23). This can be specified as the PARAM tag
- "port".</DL>
-<HR>
-
-<H3>
-proxy</H3>
-<PRE>
-java.lang.String <B>proxy</B></PRE>
-<DL>
-<DD>The proxy ip address. If this variable is set telnet will try to connect
- to this address and then send a string to tell the relay where the
- target host is.<DD><DL>
-<DT><B>See Also: </B><DD><CODE>address</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<H3>
-proxyport</H3>
-<PRE>
-int <B>proxyport</B></PRE>
-<DL>
-<DD>The proxy port number. This is the port where the relay is expected to
- listen for incoming connections.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="proxy.html"><CODE>proxy</CODE></A>, 
-<CODE>port</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<H3>
-t</H3>
-<PRE>
-java.lang.Thread <B>t</B></PRE>
-<DL>
-</DL>
-<HR>
-
-<H3>
-term</H3>
-<PRE>
-<A HREF="display/Terminal.html">Terminal</A> <B>term</B></PRE>
-<DL>
-<DD>The terminal emulation (dynamically loaded).<DD><DL>
-<DT><B>See Also: </B><DD><CODE>emulation</CODE>, 
-<A HREF="display/Terminal.html"><CODE>Terminal</CODE></A>, 
-<A HREF="display/TerminalHost.html"><CODE>TerminalHost</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<H3>
-tio</H3>
-<PRE>
-<A HREF="socket/TelnetIO.html">TelnetIO</A> <B>tio</B></PRE>
-<DL>
-<DD>The telnet io methods.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="socket/TelnetIO.html"><CODE>TelnetIO</CODE></A></DL>
-</DD>
-</DL>
-
-<P>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Package</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/StatusPeer.html b/web/root/telnet/Documentation/Source/socket/StatusPeer.html
deleted file mode 100644
index 8b3989b4ef..0000000000
--- a/web/root/telnet/Documentation/Source/socket/StatusPeer.html
+++ /dev/null
@@ -1,181 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:35 CEST 1999 -->
-<TITLE>
-: Interface  StatusPeer
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="StatusPeer.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-socket</FONT>
-<BR>
-Interface  StatusPeer</H2>
-<DL>
-<DT><B>All Known Implementing Classes:</B> <DD><A HREF="../telnet.html">telnet</A>, <A HREF="../socket/TelnetIO.html">TelnetIO</A></DD>
-</DL>
-<HR>
-<DL>
-<DT>public abstract interface <B>StatusPeer</B></DL>
-
-<P>
-StatusPeer -- interface for status messages
- --
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: StatusPeer.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-</DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.Object</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/StatusPeer.html#notifyStatus(java.util.Vector)">notifyStatus</A></B>(java.util.Vector&nbsp;status)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called for the peer of the TelnetIO class if there is
- a statuschange.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="notifyStatus(java.util.Vector)"><!-- --></A><H3>
-notifyStatus</H3>
-<PRE>
-public java.lang.Object <B>notifyStatus</B>(java.util.Vector&nbsp;status)</PRE>
-<DL>
-<DD>This method is called for the peer of the TelnetIO class if there is
- a statuschange.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>status</CODE> - A Vector containing the key as element 0 and any arguments
-               from element 1 on.<DT><B>Returns:</B><DD>an object that matches the requested information or null<DT><B>See Also: </B><DD><A HREF="../socket/TelnetIO.html"><CODE>TelnetIO</CODE></A></DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="StatusPeer.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;CONSTR&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/TelnetIO.html b/web/root/telnet/Documentation/Source/socket/TelnetIO.html
deleted file mode 100644
index 2b41ba3088..0000000000
--- a/web/root/telnet/Documentation/Source/socket/TelnetIO.html
+++ /dev/null
@@ -1,408 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:36 CEST 1999 -->
-<TITLE>
-: Class  TelnetIO
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="../socket/TelnetWrapper.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TelnetIO.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-socket</FONT>
-<BR>
-Class  TelnetIO</H2>
-<PRE>
-java.lang.Object
-  |
-  +--<B>socket.TelnetIO</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>TelnetIO</B><DT>extends java.lang.Object<DT>implements <A HREF="../socket/StatusPeer.html">StatusPeer</A></DL>
-
-<P>
-Implements simple telnet io
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: TelnetIO.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-</DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#TelnetIO()">TelnetIO</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#available()">available</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns bytes available to be read.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#connect(java.lang.String)">connect</A></B>(java.lang.String&nbsp;address)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connect to the remote host at the default telnet port (23).</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;address,
-        int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connect to the remote host at the specified port.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Disconnect from remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.Object</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#notifyStatus(java.util.Vector)">notifyStatus</A></B>(java.util.Vector&nbsp;status)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Notify about current telnet status.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;byte[]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#receive()">receive</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Read data from the remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#send(byte)">send</A></B>(byte&nbsp;b)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#send(byte[])">send</A></B>(byte[]&nbsp;buf)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Send data to the remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#setPeer(socket.StatusPeer)">setPeer</A></B>(<A HREF="../socket/StatusPeer.html">StatusPeer</A>&nbsp;obj)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the object to be notified about current status.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetIO.html#toString()">toString</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return the version of TelnetIO.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="TelnetIO()"><!-- --></A><H3>
-TelnetIO</H3>
-<PRE>
-public <B>TelnetIO</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="toString()"><!-- --></A><H3>
-toString</H3>
-<PRE>
-public java.lang.String <B>toString</B>()</PRE>
-<DL>
-<DD>Return the version of TelnetIO.<DD><DL>
-<DT><B>Overrides:</B><DD>toString in class java.lang.Object</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;address,
-                    int&nbsp;port)
-             throws java.io.IOException</PRE>
-<DL>
-<DD>Connect to the remote host at the specified port.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>address</CODE> - the symbolic host address<DD><CODE>port</CODE> - the numeric port<DT><B>See Also: </B><DD><A HREF="../socket/TelnetIO.html#disconnect()"><CODE>disconnect()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()
-                throws java.io.IOException</PRE>
-<DL>
-<DD>Disconnect from remote host.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../socket/TelnetIO.html#connect(java.lang.String, int)"><CODE>connect(java.lang.String, int)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public void <B>connect</B>(java.lang.String&nbsp;address)
-             throws java.io.IOException</PRE>
-<DL>
-<DD>Connect to the remote host at the default telnet port (23).<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>address</CODE> - the symbolic host address</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setPeer(socket.StatusPeer)"><!-- --></A><H3>
-setPeer</H3>
-<PRE>
-public void <B>setPeer</B>(<A HREF="../socket/StatusPeer.html">StatusPeer</A>&nbsp;obj)</PRE>
-<DL>
-<DD>Set the object to be notified about current status.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>obj</CODE> - object to be notified.</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="available()"><!-- --></A><H3>
-available</H3>
-<PRE>
-public int <B>available</B>()
-              throws java.io.IOException</PRE>
-<DL>
-<DD>Returns bytes available to be read.  Since they haven't been
- negotiated over, this could be misleading.
- Most useful as a boolean value - "are any bytes available" -
- rather than as an exact count of "how many ara available."<DD><DL>
-<DT><B>Throws:</B><DD>java.io.IOException - on problems with the socket connection</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receive()"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public byte[] <B>receive</B>()
-               throws java.io.IOException</PRE>
-<DL>
-<DD>Read data from the remote host. Blocks until data is available. 
- Returns an array of bytes.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../socket/TelnetIO.html#send(byte[])"><CODE>send(byte[])</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="send(byte[])"><!-- --></A><H3>
-send</H3>
-<PRE>
-public void <B>send</B>(byte[]&nbsp;buf)
-          throws java.io.IOException</PRE>
-<DL>
-<DD>Send data to the remote host.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>buf</CODE> - array of bytes to send<DT><B>See Also: </B><DD><A HREF="../socket/TelnetIO.html#receive()"><CODE>receive()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="send(byte)"><!-- --></A><H3>
-send</H3>
-<PRE>
-public void <B>send</B>(byte&nbsp;b)
-          throws java.io.IOException</PRE>
-<DL>
-<DD><DL>
-</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="notifyStatus(java.util.Vector)"><!-- --></A><H3>
-notifyStatus</H3>
-<PRE>
-public java.lang.Object <B>notifyStatus</B>(java.util.Vector&nbsp;status)</PRE>
-<DL>
-<DD>Notify about current telnet status. This method is called top-down.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="../socket/StatusPeer.html#notifyStatus(java.util.Vector)">notifyStatus</A> in interface <A HREF="../socket/StatusPeer.html">StatusPeer</A><DT><B>Parameters:</B><DD><CODE>status</CODE> - contains status information</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;<A HREF="../socket/TelnetWrapper.html"><B>NEXT CLASS</B></A></FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TelnetIO.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/TelnetWrapper.html b/web/root/telnet/Documentation/Source/socket/TelnetWrapper.html
deleted file mode 100644
index e035970ef8..0000000000
--- a/web/root/telnet/Documentation/Source/socket/TelnetWrapper.html
+++ /dev/null
@@ -1,679 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:36 CEST 1999 -->
-<TITLE>
-: Class  TelnetWrapper
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../socket/TelnetIO.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TelnetWrapper.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-socket</FONT>
-<BR>
-Class  TelnetWrapper</H2>
-<PRE>
-java.lang.Object
-  |
-  +--<B>socket.TelnetWrapper</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>TelnetWrapper</B><DT>extends java.lang.Object</DL>
-
-<P>
-Wrapper for a Java Telnet call. 
- To use, make a new TelnetWrapper() with the name or IP address of a host.
- Then, for most uses, the easiest way is to call setPrompt() with the
- expected prompt, then call login(), and a sequence of sendLine()'s
- until you get what you want done.
- <P>
- If you don't know the prompt ahead of time, you have to do a sequence of
- send() and wait() or receiveUntil() calls.  send() sends a string across
- the telnet connection. Add a '\r' to the end if you want to
- complete a command. wait() waits for an exact string from the other side
- of the telnet connection, and returns nothing,
- receiveUntil() also waits for a string, but returns all the data
- that it received while waiting, including the string itself. 
- Use this if you want the output from a command. Please note that
- the telnet connection will usually echo the sent command. 
- <P>
- sendLine() is generally better, since it adds the '\r'
- automatically, waits for the prompt before returning, and returns all
- data received before the prompt, with the prompt itself cut off the
- end, and the sent command cut off the beginning. login() and
- sendLine() are implemented using send(), wait() and receiveUntil().
- They can be freely mixed and matched.
- <P>
- Here is a simple example of the use of TelnetWrapper:
- <PRE>
- // creates a new file in /tmp, lists the directory to prove it done
- {
-   TelnetWrapper telnet = new TelnetWrapper("123.45.78.90");
-
-   // setting the correct prompt ahead of time is very important 
-   // if you want to use login and sendLine
-   telnet.setPrompt("$ ");
-   telnet.login("loginname", "password");
-
-   // this is how you have to do it otherwise
-   telnet.send("touch /tmp/TELNET_WRAPPER" + "\r");
-   telnet.wait("$ ");
-
-   // sendLine 1: adds the \r automatically, 2: waits for the prompt
-   // before returning 3: returns what was printed from the command
-   String ls = telnet.sendLine("ls /tmp");
-   System.out.println(ls);
-
-   // clean up
-   telnet.disconnect();
- } 
- </PRE>
-<P>
-<DL>
-<DT><B>Version: </B><DD>0.2 5/15/97 - added comments, replaced String += with
-    StringBuffer.append() in receiveUntil(), added port constructor</DD>
-<DT><B>Author: </B><DD>George Ruban 3/4/97</DD>
-<DT><B>See Also: </B><DD><A HREF="../socket/TelnetIO.html"><CODE>TelnetIO</CODE></A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="field_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Field Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#debug">debug</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set to true for System.out.println debugging.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#TelnetWrapper(java.lang.String)">TelnetWrapper</A></B>(java.lang.String&nbsp;host)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connects to the default telnet port on the given host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#TelnetWrapper(java.lang.String, int)">TelnetWrapper</A></B>(java.lang.String&nbsp;host,
-              int&nbsp;port)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connects to a specific telnet port on the given host.</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#available()">available</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns bytes available to be read.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ends the telnet connection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#finalize()">finalize</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ends the telnet connection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#login(java.lang.String, java.lang.String)">login</A></B>(java.lang.String&nbsp;loginName,
-      java.lang.String&nbsp;password)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Logs in as a particular user and password.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;args)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Telnet test driver.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#receive()">receive</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns a String from the telnet connection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;byte[]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#receiveBytes()">receiveBytes</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns a byte array.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#receiveUntil(java.lang.String)">receiveUntil</A></B>(java.lang.String&nbsp;token)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns all data received up until a certain token.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#receiveUntil(java.lang.String, long)">receiveUntil</A></B>(java.lang.String&nbsp;token,
-             long&nbsp;timeout)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns all data received up until a certain token.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#send(byte[])">send</A></B>(byte[]&nbsp;buf)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sends bytes over the telnet connection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#send(java.lang.String)">send</A></B>(java.lang.String&nbsp;s)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sends a String to the remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#sendLine(java.lang.String)">sendLine</A></B>(java.lang.String&nbsp;command)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sends a line to the remote host, returns all data before the prompt.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#setDefaultPrompt(java.lang.String)">setDefaultPrompt</A></B>(java.lang.String&nbsp;prompt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets the default prompt used by all TelnetWrappers.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#setLogin(java.lang.String, java.lang.String)">setLogin</A></B>(java.lang.String&nbsp;login,
-         java.lang.String&nbsp;password)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets the default login used by TelnetWrappers.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#setPrompt(java.lang.String)">setPrompt</A></B>(java.lang.String&nbsp;prompt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets the expected prompt.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#unsetLogin()">unsetLogin</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Turns off the default login of TelnetWrappers.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#wait(java.lang.String)">wait</A></B>(java.lang.String&nbsp;token)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Skip any received data until the token appears.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="../socket/TelnetWrapper.html#wait(java.lang.String, long)">wait</A></B>(java.lang.String&nbsp;token,
-     long&nbsp;timeout)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Wait for a String or a timeout.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-toString, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-<A NAME="field_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Field Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="debug"><!-- --></A><H3>
-debug</H3>
-<PRE>
-public boolean <B>debug</B></PRE>
-<DL>
-<DD>Set to true for System.out.println debugging.</DL>
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="TelnetWrapper(java.lang.String)"><!-- --></A><H3>
-TelnetWrapper</H3>
-<PRE>
-public <B>TelnetWrapper</B>(java.lang.String&nbsp;host)
-              throws java.io.IOException</PRE>
-<DL>
-<DD>Connects to the default telnet port on the given host. 
- If the defaultLogin and defaultPassword are non-null, attempts login.</DL>
-<HR>
-
-<A NAME="TelnetWrapper(java.lang.String, int)"><!-- --></A><H3>
-TelnetWrapper</H3>
-<PRE>
-public <B>TelnetWrapper</B>(java.lang.String&nbsp;host,
-                     int&nbsp;port)
-              throws java.io.IOException</PRE>
-<DL>
-<DD>Connects to a specific telnet port on the given host. 
- If the defaultLogin and defaultPassword are non-null, attempts login.</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="wait(java.lang.String)"><!-- --></A><H3>
-wait</H3>
-<PRE>
-public void <B>wait</B>(java.lang.String&nbsp;token)
-          throws java.io.IOException</PRE>
-<DL>
-<DD>Skip any received data until the token appears. 
- More efficient than receiveUntil, but liable to fail on large
- tokens that can be spread over several "send"s. In that case,
- consider using receiveUntil and ignoring the return value.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>token</CODE> - String to wait for<DT><B>Throws:</B><DD>java.io.IOException - on problems with the socket connection<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#receiveUntil(java.lang.String)"><CODE>receiveUntil(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="wait(java.lang.String, long)"><!-- --></A><H3>
-wait</H3>
-<PRE>
-public void <B>wait</B>(java.lang.String&nbsp;token,
-                 long&nbsp;timeout)
-          throws java.io.IOException,
-                 <A HREF="../socket/TimedOutException.html">TimedOutException</A></PRE>
-<DL>
-<DD>Wait for a String or a timeout. 
- If time runs out, throws a TimedOutException.
- Sleeps in intervals of 100 milliseconds until either receiving the
- token or timeout.
- <P>
- More efficient than receiveUntil, but liable to fail on large
- tokens that can be spread over several "send"s. In that case,
- consider using receiveUntil and ignoring the return value.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>token</CODE> - String to wait for<DD><CODE>timeout</CODE> - time in milliseconds to wait (negative means wait forever)<DT><B>Throws:</B><DD>java.io.IOException - on problems with the socket connection<DD><A HREF="../socket/TimedOutException.html">TimedOutException</A> - if time runs out before token received<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#receiveUntil(java.lang.String, long)"><CODE>receiveUntil(String, long)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="available()"><!-- --></A><H3>
-available</H3>
-<PRE>
-public int <B>available</B>()
-              throws java.io.IOException</PRE>
-<DL>
-<DD>Returns bytes available to be read.  Since they haven't been
- negotiated over, this could be misleading...</DL>
-<HR>
-
-<A NAME="receive()"><!-- --></A><H3>
-receive</H3>
-<PRE>
-public java.lang.String <B>receive</B>()
-                         throws java.io.IOException</PRE>
-<DL>
-<DD>Returns a String from the telnet connection. Blocks
- until one is available. No guarantees that the string is in
- any way complete.
- NOTE: uses Java 1.0.2 style String-bytes conversion.</DL>
-<HR>
-
-<A NAME="receiveBytes()"><!-- --></A><H3>
-receiveBytes</H3>
-<PRE>
-public byte[] <B>receiveBytes</B>()
-                    throws java.io.IOException</PRE>
-<DL>
-<DD>Returns a byte array. Blocks until data is available.</DL>
-<HR>
-
-<A NAME="receiveUntil(java.lang.String)"><!-- --></A><H3>
-receiveUntil</H3>
-<PRE>
-public java.lang.String <B>receiveUntil</B>(java.lang.String&nbsp;token)
-                              throws java.io.IOException</PRE>
-<DL>
-<DD>Returns all data received up until a certain token.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>token</CODE> - String to wait for<DT><B>Throws:</B><DD>java.io.IOException - on problems with the socket connection<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#wait(java.lang.String)"><CODE>wait(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="receiveUntil(java.lang.String, long)"><!-- --></A><H3>
-receiveUntil</H3>
-<PRE>
-public java.lang.String <B>receiveUntil</B>(java.lang.String&nbsp;token,
-                                     long&nbsp;timeout)
-                              throws java.io.IOException,
-                                     <A HREF="../socket/TimedOutException.html">TimedOutException</A></PRE>
-<DL>
-<DD>Returns all data received up until a certain token.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>token</CODE> - String to wait for<DD><CODE>timeout</CODE> - time in milliseconds to wait (negative means wait forever)<DT><B>Throws:</B><DD>java.io.IOException - on problems with the socket connection<DD><A HREF="../socket/TimedOutException.html">TimedOutException</A> - if time runs out before token received<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#wait(java.lang.String, long)"><CODE>wait(String, long)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="send(java.lang.String)"><!-- --></A><H3>
-send</H3>
-<PRE>
-public void <B>send</B>(java.lang.String&nbsp;s)
-          throws java.io.IOException</PRE>
-<DL>
-<DD>Sends a String to the remote host.
- NOTE: uses Java 1.0.2 style String-bytes conversion.<DD><DL>
-<DT><B>Throws:</B><DD>java.io.IOException - on problems with the socket connection</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="sendLine(java.lang.String)"><!-- --></A><H3>
-sendLine</H3>
-<PRE>
-public java.lang.String <B>sendLine</B>(java.lang.String&nbsp;command)
-                          throws java.io.IOException</PRE>
-<DL>
-<DD>Sends a line to the remote host, returns all data before the prompt.
- Since telnet seems to rely on carriage returns ('\r'), 
- one will be appended to the sent string, if necessary.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>command</CODE> - command line to send<DT><B>Returns:</B><DD>whatever data the command produced before the prompt.<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#setPrompt(java.lang.String)"><CODE>setPrompt(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="send(byte[])"><!-- --></A><H3>
-send</H3>
-<PRE>
-public void <B>send</B>(byte[]&nbsp;buf)
-          throws java.io.IOException</PRE>
-<DL>
-<DD>Sends bytes over the telnet connection.</DL>
-<HR>
-
-<A NAME="login(java.lang.String, java.lang.String)"><!-- --></A><H3>
-login</H3>
-<PRE>
-public void <B>login</B>(java.lang.String&nbsp;loginName,
-                  java.lang.String&nbsp;password)
-           throws java.io.IOException</PRE>
-<DL>
-<DD>Logs in as a particular user and password. 
- Returns after receiving prompt.</DL>
-<HR>
-
-<A NAME="setPrompt(java.lang.String)"><!-- --></A><H3>
-setPrompt</H3>
-<PRE>
-public void <B>setPrompt</B>(java.lang.String&nbsp;prompt)</PRE>
-<DL>
-<DD>Sets the expected prompt. 
- If this function is not explicitly called, the default prompt is used.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#setDefaultPrompt(java.lang.String)"><CODE>setDefaultPrompt(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setDefaultPrompt(java.lang.String)"><!-- --></A><H3>
-setDefaultPrompt</H3>
-<PRE>
-public static void <B>setDefaultPrompt</B>(java.lang.String&nbsp;prompt)</PRE>
-<DL>
-<DD>Sets the default prompt used by all TelnetWrappers.
- This can be specifically overridden for a specific instance.
- The default prompt starts out as "$ " until this function is called.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#setPrompt(java.lang.String)"><CODE>setPrompt(java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="setLogin(java.lang.String, java.lang.String)"><!-- --></A><H3>
-setLogin</H3>
-<PRE>
-public static void <B>setLogin</B>(java.lang.String&nbsp;login,
-                            java.lang.String&nbsp;password)</PRE>
-<DL>
-<DD>Sets the default login used by TelnetWrappers.
- If this method is called with non-null login and password,
- all TelnetWrappers will attempt to login when first created.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>login</CODE> - login name to use<DD><CODE>password</CODE> - password to use<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#login(java.lang.String, java.lang.String)"><CODE>login(java.lang.String, java.lang.String)</CODE></A>, 
-<A HREF="../socket/TelnetWrapper.html#unsetLogin()"><CODE>unsetLogin()</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="unsetLogin()"><!-- --></A><H3>
-unsetLogin</H3>
-<PRE>
-public static void <B>unsetLogin</B>()</PRE>
-<DL>
-<DD>Turns off the default login of TelnetWrappers.
- After this method is called, TelnetWrappers will not
- login until that method is explicitly called.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="../socket/TelnetWrapper.html#setLogin(java.lang.String, java.lang.String)"><CODE>setLogin(java.lang.String, java.lang.String)</CODE></A>, 
-<A HREF="../socket/TelnetWrapper.html#login(java.lang.String, java.lang.String)"><CODE>login(java.lang.String, java.lang.String)</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public void <B>disconnect</B>()
-                throws java.io.IOException</PRE>
-<DL>
-<DD>Ends the telnet connection.</DL>
-<HR>
-
-<A NAME="finalize()"><!-- --></A><H3>
-finalize</H3>
-<PRE>
-public void <B>finalize</B>()</PRE>
-<DL>
-<DD>Ends the telnet connection.<DD><DL>
-<DT><B>Overrides:</B><DD>finalize in class java.lang.Object</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="main(java.lang.String[])"><!-- --></A><H3>
-main</H3>
-<PRE>
-public static void <B>main</B>(java.lang.String[]&nbsp;args)
-                 throws java.io.IOException</PRE>
-<DL>
-<DD>Telnet test driver.
- Modeled after the IOtest.java example in the Telnet Applet.
- Logs in to "host", creates a timestamped file in /tmp, lists the
- /tmp directory to System.out, disconnects.  Shows off several
- TelnetWrapper methods.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>args</CODE> - host login password prompt</DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../socket/TelnetIO.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TelnetWrapper.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/TimedOutException.html b/web/root/telnet/Documentation/Source/socket/TimedOutException.html
deleted file mode 100644
index 4f180a154c..0000000000
--- a/web/root/telnet/Documentation/Source/socket/TimedOutException.html
+++ /dev/null
@@ -1,228 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:36 CEST 1999 -->
-<TITLE>
-: Class  TimedOutException
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TimedOutException.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#methods_inherited_from_class_java.lang.Throwable">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;METHOD</FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-<FONT SIZE="-1">
-socket</FONT>
-<BR>
-Class  TimedOutException</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.lang.Throwable
-        |
-        +--java.lang.Exception
-              |
-              +--java.io.IOException
-                    |
-                    +--<B>socket.TimedOutException</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>TimedOutException</B><DT>extends java.io.IOException</DL>
-
-<P>
-Exception thrown when a Telnet connection takes too long
- before receiving a specified String token.
-<P>
-<DL>
-<DT><B>Version: </B><DD>0.1 7/30/97</DD>
-<DT><B>Author: </B><DD>George Ruban</DD>
-<DT><B>See Also: </B><DD><A HREF="../serialized-form.html#socket.TimedOutException">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../socket/TimedOutException.html#TimedOutException()">TimedOutException</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="../socket/TimedOutException.html#TimedOutException(java.lang.String)">TimedOutException</A></B>(java.lang.String&nbsp;message)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="methods_inherited_from_class_java.lang.Throwable"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Throwable</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>fillInStackTrace, 
-getLocalizedMessage, 
-getMessage, 
-printStackTrace, 
-printStackTrace, 
-printStackTrace, 
-toString</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="TimedOutException()"><!-- --></A><H3>
-TimedOutException</H3>
-<PRE>
-public <B>TimedOutException</B>()</PRE>
-<DL>
-</DL>
-<HR>
-
-<A NAME="TimedOutException(java.lang.String)"><!-- --></A><H3>
-TimedOutException</H3>
-<PRE>
-public <B>TimedOutException</B>(java.lang.String&nbsp;message)</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;PREV CLASS&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="TimedOutException.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#methods_inherited_from_class_java.lang.Throwable">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;METHOD</FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/package-frame.html b/web/root/telnet/Documentation/Source/socket/package-frame.html
deleted file mode 100644
index 9fb033f62a..0000000000
--- a/web/root/telnet/Documentation/Source/socket/package-frame.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Package socket
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-<FONT size="+1" ID="FrameTitleFont">
-<A HREF="../socket/package-summary.html" TARGET="classFrame">socket</A></FONT>
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Interfaces</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="StatusPeer.html" TARGET="classFrame"><I>StatusPeer</I></A></FONT></TD>
-</TR>
-</TABLE>
-
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Classes</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="TelnetIO.html" TARGET="classFrame">TelnetIO</A>
-<BR>
-<A HREF="TelnetWrapper.html" TARGET="classFrame">TelnetWrapper</A></FONT></TD>
-</TR>
-</TABLE>
-
-
-<TABLE BORDER="0" WIDTH="100%">
-<TR>
-<TD NOWRAP><FONT size="+1" ID="FrameHeadingFont">
-Exceptions</FONT>&nbsp;
-<FONT ID="FrameItemFont">
-<BR>
-<A HREF="TimedOutException.html" TARGET="classFrame">TimedOutException</A></FONT></TD>
-</TR>
-</TABLE>
-
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/package-summary.html b/web/root/telnet/Documentation/Source/socket/package-summary.html
deleted file mode 100644
index 9e552b3660..0000000000
--- a/web/root/telnet/Documentation/Source/socket/package-summary.html
+++ /dev/null
@@ -1,138 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: Package socket
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/package-summary.html"><B>PREV PACKAGE</B></A>&nbsp;
-&nbsp;NEXT PACKAGE</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<H2>
-Package socket
-</H2>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Interface Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="StatusPeer.html"><I>StatusPeer</I></A></B></TD>
-<TD>StatusPeer -- interface for status messages
- --</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Class Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="TelnetIO.html">TelnetIO</A></B></TD>
-<TD>Implements simple telnet io</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="TelnetWrapper.html">TelnetWrapper</A></B></TD>
-<TD>Wrapper for a Java Telnet call.</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Exception Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD WIDTH="15%"><B><A HREF="TimedOutException.html">TimedOutException</A></B></TD>
-<TD>Exception thrown when a Telnet connection takes too long
- before receiving a specified String token.</TD>
-</TR>
-</TABLE>
-&nbsp;
-
-<P>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/package-summary.html"><B>PREV PACKAGE</B></A>&nbsp;
-&nbsp;NEXT PACKAGE</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/socket/package-tree.html b/web/root/telnet/Documentation/Source/socket/package-tree.html
deleted file mode 100644
index 166ea991e8..0000000000
--- a/web/root/telnet/Documentation/Source/socket/package-tree.html
+++ /dev/null
@@ -1,115 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:32 CEST 1999 -->
-<TITLE>
-: socket Class Hierarchy
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="../stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/package-tree.html"><B>PREV</B></A>&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<CENTER>
-<H2>
-Hierarchy For Package socket
-</H2>
-</CENTER>
-<DL>
-<DT><B>Package Hierarchies: </B><DD><A HREF="../overview-tree.html">All Packages</A></DL>
-<HR>
-<H2>
-Class Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">class java.lang.Object<UL>
-<LI TYPE="circle">class socket.<A HREF="../socket/TelnetIO.html"><B>TelnetIO</B></A> (implements socket.<A HREF="../socket/StatusPeer.html">StatusPeer</A>)
-<LI TYPE="circle">class socket.<A HREF="../socket/TelnetWrapper.html"><B>TelnetWrapper</B></A><LI TYPE="circle">class java.lang.Throwable (implements java.io.Serializable)
-<UL>
-<LI TYPE="circle">class java.lang.Exception<UL>
-<LI TYPE="circle">class java.io.IOException<UL>
-<LI TYPE="circle">class socket.<A HREF="../socket/TimedOutException.html"><B>TimedOutException</B></A></UL>
-</UL>
-</UL>
-</UL>
-</UL>
-<H2>
-Interface Hierarchy
-</H2>
-<UL>
-<LI TYPE="circle">interface socket.<A HREF="../socket/StatusPeer.html"><B>StatusPeer</B></A></UL>
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <FONT ID="NavBarFont1">Class</FONT>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="../help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="../modules/package-tree.html"><B>PREV</B></A>&nbsp;
-&nbsp;NEXT</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="../index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/Source/stylesheet.css b/web/root/telnet/Documentation/Source/stylesheet.css
deleted file mode 100644
index 539689f841..0000000000
--- a/web/root/telnet/Documentation/Source/stylesheet.css
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Javadoc style sheet */
-
-/* Define colors, fonts and other style attributes here to override the defaults  */
-
-/* Page background color */
-body { background-color: #FFFFFF }
-
-/* Table colors */
-#TableHeadingColor     { background: #CCCCFF } /* Dark mauve */
-#TableSubHeadingColor  { background: #EEEEFF } /* Light mauve */
-#TableRowColor         { background: #FFFFFF } /* White */
-
-/* Font used in left-hand frame lists */
-#FrameTitleFont   { font-size: normal; font-family: normal }
-#FrameHeadingFont { font-size: normal; font-family: normal }
-#FrameItemFont    { font-size: normal; font-family: normal }
-
-/* Example of smaller, sans-serif font in frames */
-/* #FrameItemFont  { font-size: 10pt; font-family: Helvetica, Arial, sans-serif } */
-
-/* Navigation bar fonts and colors */
-#NavBarCell1    { background-color:#EEEEFF;}/* Light mauve */
-#NavBarCell1Rev { background-color:#00008B;}/* Dark Blue */
-#NavBarFont1    { font-family: Arial, Helvetica, sans-serif; color:#000000;}
-#NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
-
-#NavBarCell2    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
-#NavBarCell3    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
-
diff --git a/web/root/telnet/Documentation/Source/telnet.html b/web/root/telnet/Documentation/Source/telnet.html
deleted file mode 100644
index a780c05a3a..0000000000
--- a/web/root/telnet/Documentation/Source/telnet.html
+++ /dev/null
@@ -1,955 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<!--NewPage-->
-<HTML>
-<HEAD>
-<!-- Generated by javadoc on Wed Jun 16 11:44:33 CEST 1999 -->
-<TITLE>
-: Class  telnet
-</TITLE>
-<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
-</HEAD>
-<BODY BGCOLOR="white">
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_top"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_top_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="proxy.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="telnet.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-<!-- ======== START OF CLASS DATA ======== -->
-<H2>
-Class  telnet</H2>
-<PRE>
-java.lang.Object
-  |
-  +--java.awt.Component
-        |
-        +--java.awt.Container
-              |
-              +--java.awt.Panel
-                    |
-                    +--java.applet.Applet
-                          |
-                          +--<B>telnet</B>
-</PRE>
-<HR>
-<DL>
-<DT>public class <B>telnet</B><DT>extends java.applet.Applet<DT>implements java.lang.Runnable, <A HREF="display/TerminalHost.html">TerminalHost</A>, <A HREF="socket/StatusPeer.html">StatusPeer</A></DL>
-
-<P>
-A telnet implementation that supports different terminal emulations.
-<P>
-<DL>
-<DT><B>Version: </B><DD>$Id: telnet.html,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $</DD>
-<DT><B>Author: </B><DD>Matthias L. Jugel, Marcus Mei�ner</DD>
-<DT><B>See Also: </B><DD><A HREF="serialized-form.html#telnet">Serialized Form</A></DL>
-<HR>
-
-<P>
-<!-- ======== INNER CLASS SUMMARY ======== -->
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-
-<A NAME="field_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Field Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#address">address</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The host address to connect to.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#emulation">emulation</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Emulation type (default is vt320).</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;java.util.Vector</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#modules">modules</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dynamically loaded modules are stored here.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.util.Hashtable</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#params">params</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This Hashtable contains information retrievable by getParameter() in case
- the program is run as an application and the AppletStub is missing.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#port">port</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The port number (default ist 23).</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#proxy">proxy</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The proxy ip address.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;int</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#proxyport">proxyport</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The proxy port number.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;<A HREF="display/Terminal.html">Terminal</A></CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#term">term</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The terminal emulation (dynamically loaded).</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>protected &nbsp;<A HREF="socket/TelnetIO.html">TelnetIO</A></CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#tio">tio</A></B></CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The telnet io methods.</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="fields_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Fields inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>BOTTOM_ALIGNMENT,  
-CENTER_ALIGNMENT,  
-LEFT_ALIGNMENT,  
-RIGHT_ALIGNMENT,  
-TOP_ALIGNMENT</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ======== CONSTRUCTOR SUMMARY ======== -->
-
-<A NAME="constructor_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Constructor Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE><B><A HREF="telnet.html#telnet()">telnet</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
-</TR>
-</TABLE>
-&nbsp;
-<!-- ========== METHOD SUMMARY =========== -->
-
-<A NAME="method_summary"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=2><FONT SIZE="+2">
-<B>Method Summary</B></FONT></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#connect()">connect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connect to the specified host and port but don't break existing 
- connections.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#connect(java.lang.String)">connect</A></B>(java.lang.String&nbsp;host)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connect to the specified host and port but don't break existing 
- connections.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#connect(java.lang.String, int)">connect</A></B>(java.lang.String&nbsp;host,
-        int&nbsp;prt)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connect to the specified host and port but don't break existing 
- connections.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#disconnect()">disconnect</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Disconnect from the remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#getAppletInfo()">getAppletInfo</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Retrieve the current version of the applet.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#getParameter(java.lang.String)">getParameter</A></B>(java.lang.String&nbsp;name)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;We override the Applet method getParameter() to be able to handle 
- parameters even as application.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.String[][]</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#getParameterInfo()">getParameterInfo</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Retrieve parameter tag information.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#init()">init</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initialize applet.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>static&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;args)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The main function is called on startup of the application.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;java.lang.Object</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#notifyStatus(java.util.Vector)">notifyStatus</A></B>(java.util.Vector&nbsp;status)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This method is called when telnet needs to be notified of status changes.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#run()">run</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Try to read data from the sockets and put it on the terminal.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#send(java.lang.String)">send</A></B>(java.lang.String&nbsp;str)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Send a String to the remote host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#start()">start</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Upon start of the applet try to create a new connection.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#stop()">stop</A></B>()</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Disconnect when the applet is stopped.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;boolean</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#writeToSocket(java.lang.String)">writeToSocket</A></B>(java.lang.String&nbsp;str)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Send a String to the remote Host.</TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
-<CODE>&nbsp;void</CODE></FONT></TD>
-<TD><CODE><B><A HREF="telnet.html#writeToUser(java.lang.String)">writeToUser</A></B>(java.lang.String&nbsp;str)</CODE>
-
-<BR>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Send a String to the users terminal</TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.applet.Applet"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.applet.Applet</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>destroy, 
-getAppletContext, 
-getAudioClip, 
-getAudioClip, 
-getCodeBase, 
-getDocumentBase, 
-getImage, 
-getImage, 
-getLocale, 
-isActive, 
-newAudioClip, 
-play, 
-play, 
-resize, 
-resize, 
-setStub, 
-showStatus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Panel"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Panel</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>addNotify</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Container"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Container</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>add, 
-add, 
-add, 
-add, 
-add, 
-addContainerListener, 
-addImpl, 
-countComponents, 
-deliverEvent, 
-doLayout, 
-findComponentAt, 
-findComponentAt, 
-getAlignmentX, 
-getAlignmentY, 
-getComponent, 
-getComponentAt, 
-getComponentAt, 
-getComponentCount, 
-getComponents, 
-getInsets, 
-getLayout, 
-getMaximumSize, 
-getMinimumSize, 
-getPreferredSize, 
-insets, 
-invalidate, 
-isAncestorOf, 
-layout, 
-list, 
-list, 
-locate, 
-minimumSize, 
-paint, 
-paintComponents, 
-paramString, 
-preferredSize, 
-print, 
-printComponents, 
-processContainerEvent, 
-processEvent, 
-remove, 
-remove, 
-removeAll, 
-removeContainerListener, 
-removeNotify, 
-setFont, 
-setLayout, 
-update, 
-validate, 
-validateTree</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.awt.Component"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.awt.Component</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>action, 
-add, 
-addComponentListener, 
-addFocusListener, 
-addInputMethodListener, 
-addKeyListener, 
-addMouseListener, 
-addMouseMotionListener, 
-addPropertyChangeListener, 
-addPropertyChangeListener, 
-bounds, 
-checkImage, 
-checkImage, 
-coalesceEvents, 
-contains, 
-contains, 
-createImage, 
-createImage, 
-disable, 
-disableEvents, 
-dispatchEvent, 
-enable, 
-enable, 
-enableEvents, 
-enableInputMethods, 
-firePropertyChange, 
-getBackground, 
-getBounds, 
-getBounds, 
-getColorModel, 
-getComponentOrientation, 
-getCursor, 
-getDropTarget, 
-getFont, 
-getFontMetrics, 
-getForeground, 
-getGraphics, 
-getHeight, 
-getInputContext, 
-getInputMethodRequests, 
-getLocation, 
-getLocation, 
-getLocationOnScreen, 
-getName, 
-getParent, 
-getPeer, 
-getSize, 
-getSize, 
-getToolkit, 
-getTreeLock, 
-getWidth, 
-getX, 
-getY, 
-gotFocus, 
-handleEvent, 
-hasFocus, 
-hide, 
-imageUpdate, 
-inside, 
-isDisplayable, 
-isDoubleBuffered, 
-isEnabled, 
-isFocusTraversable, 
-isLightweight, 
-isOpaque, 
-isShowing, 
-isValid, 
-isVisible, 
-keyDown, 
-keyUp, 
-list, 
-list, 
-list, 
-location, 
-lostFocus, 
-mouseDown, 
-mouseDrag, 
-mouseEnter, 
-mouseExit, 
-mouseMove, 
-mouseUp, 
-move, 
-nextFocus, 
-paintAll, 
-postEvent, 
-prepareImage, 
-prepareImage, 
-printAll, 
-processComponentEvent, 
-processFocusEvent, 
-processInputMethodEvent, 
-processKeyEvent, 
-processMouseEvent, 
-processMouseMotionEvent, 
-remove, 
-removeComponentListener, 
-removeFocusListener, 
-removeInputMethodListener, 
-removeKeyListener, 
-removeMouseListener, 
-removeMouseMotionListener, 
-removePropertyChangeListener, 
-removePropertyChangeListener, 
-repaint, 
-repaint, 
-repaint, 
-repaint, 
-requestFocus, 
-reshape, 
-setBackground, 
-setBounds, 
-setBounds, 
-setComponentOrientation, 
-setCursor, 
-setDropTarget, 
-setEnabled, 
-setForeground, 
-setLocale, 
-setLocation, 
-setLocation, 
-setName, 
-setSize, 
-setSize, 
-setVisible, 
-show, 
-show, 
-size, 
-toString, 
-transferFocus</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#EEEEFF" ID="TableSubHeadingColor">
-<TD><B>Methods inherited from class java.lang.Object</B></TD>
-</TR>
-<TR BGCOLOR="white" ID="TableRowColor">
-<TD><CODE>clone, 
-equals, 
-finalize, 
-getClass, 
-hashCode, 
-notify, 
-notifyAll, 
-wait, 
-wait, 
-wait</CODE></TD>
-</TR>
-</TABLE>
-&nbsp;
-<P>
-
-<!-- ============ FIELD DETAIL =========== -->
-
-<A NAME="field_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Field Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="tio"><!-- --></A><H3>
-tio</H3>
-<PRE>
-protected <A HREF="socket/TelnetIO.html">TelnetIO</A> <B>tio</B></PRE>
-<DL>
-<DD>The telnet io methods.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="socket/TelnetIO.html"><CODE>TelnetIO</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="term"><!-- --></A><H3>
-term</H3>
-<PRE>
-protected <A HREF="display/Terminal.html">Terminal</A> <B>term</B></PRE>
-<DL>
-<DD>The terminal emulation (dynamically loaded).<DD><DL>
-<DT><B>See Also: </B><DD><CODE>emulation</CODE>, 
-<A HREF="display/Terminal.html"><CODE>Terminal</CODE></A>, 
-<A HREF="display/TerminalHost.html"><CODE>TerminalHost</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="address"><!-- --></A><H3>
-address</H3>
-<PRE>
-protected java.lang.String <B>address</B></PRE>
-<DL>
-<DD>The host address to connect to. This is retrieved from the PARAM tag
- "address".</DL>
-<HR>
-
-<A NAME="port"><!-- --></A><H3>
-port</H3>
-<PRE>
-protected int <B>port</B></PRE>
-<DL>
-<DD>The port number (default ist 23). This can be specified as the PARAM tag
- "port".</DL>
-<HR>
-
-<A NAME="proxy"><!-- --></A><H3>
-proxy</H3>
-<PRE>
-protected java.lang.String <B>proxy</B></PRE>
-<DL>
-<DD>The proxy ip address. If this variable is set telnet will try to connect
- to this address and then send a string to tell the relay where the
- target host is.<DD><DL>
-<DT><B>See Also: </B><DD><CODE>address</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="proxyport"><!-- --></A><H3>
-proxyport</H3>
-<PRE>
-protected int <B>proxyport</B></PRE>
-<DL>
-<DD>The proxy port number. This is the port where the relay is expected to
- listen for incoming connections.<DD><DL>
-<DT><B>See Also: </B><DD><A HREF="proxy.html"><CODE>proxy</CODE></A>, 
-<CODE>port</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="emulation"><!-- --></A><H3>
-emulation</H3>
-<PRE>
-protected java.lang.String <B>emulation</B></PRE>
-<DL>
-<DD>Emulation type (default is vt320). This can be specified as the PARAM
- tag "emulation".<DD><DL>
-<DT><B>See Also: </B><DD><CODE>term</CODE>, 
-<A HREF="display/Terminal.html"><CODE>Terminal</CODE></A>, 
-<A HREF="display/TerminalHost.html"><CODE>TerminalHost</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="modules"><!-- --></A><H3>
-modules</H3>
-<PRE>
-protected java.util.Vector <B>modules</B></PRE>
-<DL>
-<DD>Dynamically loaded modules are stored here.</DL>
-<HR>
-
-<A NAME="params"><!-- --></A><H3>
-params</H3>
-<PRE>
-public java.util.Hashtable <B>params</B></PRE>
-<DL>
-<DD>This Hashtable contains information retrievable by getParameter() in case
- the program is run as an application and the AppletStub is missing.</DL>
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-
-<A NAME="constructor_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Constructor Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="telnet()"><!-- --></A><H3>
-telnet</H3>
-<PRE>
-public <B>telnet</B>()</PRE>
-<DL>
-</DL>
-
-<!-- ============ METHOD DETAIL ========== -->
-
-<A NAME="method_detail"><!-- --></A>
-<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
-<TR BGCOLOR="#CCCCFF" ID="TableHeadingColor">
-<TD COLSPAN=1><FONT SIZE="+2">
-<B>Method Detail</B></FONT></TD>
-</TR>
-</TABLE>
-
-<A NAME="getAppletInfo()"><!-- --></A><H3>
-getAppletInfo</H3>
-<PRE>
-public java.lang.String <B>getAppletInfo</B>()</PRE>
-<DL>
-<DD>Retrieve the current version of the applet.<DD><DL>
-<DT><B>Returns:</B><DD>String a string with the version information.<DT><B>Overrides:</B><DD>getAppletInfo in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getParameterInfo()"><!-- --></A><H3>
-getParameterInfo</H3>
-<PRE>
-public java.lang.String[][] <B>getParameterInfo</B>()</PRE>
-<DL>
-<DD>Retrieve parameter tag information. This includes the tag information from
- terminal and loaded modules.<DD><DL>
-<DT><B>Returns:</B><DD>String an array of array of string with tag information<DT><B>Overrides:</B><DD>getParameterInfo in class java.applet.Applet<DT><B>See Also: </B><DD><CODE>Applet.getParameterInfo()</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="getParameter(java.lang.String)"><!-- --></A><H3>
-getParameter</H3>
-<PRE>
-public java.lang.String <B>getParameter</B>(java.lang.String&nbsp;name)</PRE>
-<DL>
-<DD>We override the Applet method getParameter() to be able to handle 
- parameters even as application.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>name</CODE> - The name of the queried parameter.<DT><B>Returns:</B><DD>the value of the parameter<DT><B>Overrides:</B><DD>getParameter in class java.applet.Applet<DT><B>See Also: </B><DD><CODE>Applet.getParameter(java.lang.String)</CODE></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="main(java.lang.String[])"><!-- --></A><H3>
-main</H3>
-<PRE>
-public static void <B>main</B>(java.lang.String[]&nbsp;args)</PRE>
-<DL>
-<DD>The main function is called on startup of the application.<DD><DL>
-</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="init()"><!-- --></A><H3>
-init</H3>
-<PRE>
-public void <B>init</B>()</PRE>
-<DL>
-<DD>Initialize applet. This method reads the PARAM tags "address",
- "port" and "emulation". The emulation class is loaded dynamically.
- It also loads modules given as parameter "module#<nr>".<DD><DL>
-<DT><B>Overrides:</B><DD>init in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="start()"><!-- --></A><H3>
-start</H3>
-<PRE>
-public void <B>start</B>()</PRE>
-<DL>
-<DD>Upon start of the applet try to create a new connection.<DD><DL>
-<DT><B>Overrides:</B><DD>start in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="stop()"><!-- --></A><H3>
-stop</H3>
-<PRE>
-public final void <B>stop</B>()</PRE>
-<DL>
-<DD>Disconnect when the applet is stopped.<DD><DL>
-<DT><B>Overrides:</B><DD>stop in class java.applet.Applet</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="run()"><!-- --></A><H3>
-run</H3>
-<PRE>
-public void <B>run</B>()</PRE>
-<DL>
-<DD>Try to read data from the sockets and put it on the terminal.
- This is done until the thread dies or an error occurs.<DD><DL>
-<DT><B>Specified by: </B><DD>run in interface java.lang.Runnable</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect()"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public boolean <B>connect</B>()</PRE>
-<DL>
-<DD>Connect to the specified host and port but don't break existing 
- connections. Connects to the host and port specified in the tags.<DD><DL>
-<DT><B>Returns:</B><DD>false if connection was unsuccessful</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public boolean <B>connect</B>(java.lang.String&nbsp;host)</PRE>
-<DL>
-<DD>Connect to the specified host and port but don't break existing 
- connections. Uses the port specified in the tags or 23.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>host</CODE> - destination host address</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="connect(java.lang.String, int)"><!-- --></A><H3>
-connect</H3>
-<PRE>
-public boolean <B>connect</B>(java.lang.String&nbsp;host,
-                       int&nbsp;prt)</PRE>
-<DL>
-<DD>Connect to the specified host and port but don't break existing 
- connections.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>host</CODE> - destination host address<DD><CODE>prt</CODE> - destination hosts port</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="disconnect()"><!-- --></A><H3>
-disconnect</H3>
-<PRE>
-public boolean <B>disconnect</B>()</PRE>
-<DL>
-<DD>Disconnect from the remote host.<DD><DL>
-<DT><B>Returns:</B><DD>false if there was a problem disconnecting.</DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="send(java.lang.String)"><!-- --></A><H3>
-send</H3>
-<PRE>
-public boolean <B>send</B>(java.lang.String&nbsp;str)</PRE>
-<DL>
-<DD>Send a String to the remote host. Implements display.TerminalHost<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="display/TerminalHost.html#send(java.lang.String)">send</A> in interface <A HREF="display/TerminalHost.html">TerminalHost</A><DT><B>Parameters:</B><DD><CODE>s</CODE> - String to be sent<DT><B>Returns:</B><DD>true if we are connected<DT><B>See Also: </B><DD><A HREF="display/TerminalHost.html"><CODE>TerminalHost</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="writeToSocket(java.lang.String)"><!-- --></A><H3>
-writeToSocket</H3>
-<PRE>
-public boolean <B>writeToSocket</B>(java.lang.String&nbsp;str)</PRE>
-<DL>
-<DD>Send a String to the remote Host.<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>str</CODE> - String to be sent<DT><B>Returns:</B><DD>true if we are connected<DT><B>See Also: </B><DD><A HREF="modules/BSXModule.html"><CODE>BSXModule</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="writeToUser(java.lang.String)"><!-- --></A><H3>
-writeToUser</H3>
-<PRE>
-public void <B>writeToUser</B>(java.lang.String&nbsp;str)</PRE>
-<DL>
-<DD>Send a String to the users terminal<DD><DL>
-<DT><B>Parameters:</B><DD><CODE>str</CODE> - String to be displayed<DT><B>Returns:</B><DD>void<DT><B>See Also: </B><DD><A HREF="modules/BSXModule.html"><CODE>BSXModule</CODE></A></DL>
-</DD>
-</DL>
-<HR>
-
-<A NAME="notifyStatus(java.util.Vector)"><!-- --></A><H3>
-notifyStatus</H3>
-<PRE>
-public java.lang.Object <B>notifyStatus</B>(java.util.Vector&nbsp;status)</PRE>
-<DL>
-<DD>This method is called when telnet needs to be notified of status changes.<DD><DL>
-<DT><B>Specified by: </B><DD><A HREF="socket/StatusPeer.html#notifyStatus(java.util.Vector)">notifyStatus</A> in interface <A HREF="socket/StatusPeer.html">StatusPeer</A><DT><B>Parameters:</B><DD><CODE>status</CODE> - Vector of status information.<DT><B>Returns:</B><DD>an object of the information requested.<DT><B>See Also: </B><DD><A HREF="socket/StatusPeer.html"><CODE>StatusPeer</CODE></A></DL>
-</DD>
-</DL>
-<!-- ========= END OF CLASS DATA ========= -->
-<HR>
-
-<!-- ========== START OF NAVBAR ========== -->
-<A NAME="navbar_bottom"><!-- --></A>
-<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
-<TR>
-<TD COLSPAN=2 BGCOLOR="#EEEEFF" ID="NavBarCell1">
-<A NAME="navbar_bottom_firstrow"><!-- --></A>
-<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
-  <TR ALIGN="center" VALIGN="top">
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-summary.html"><FONT ID="NavBarFont1"><B>Overview</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="package-summary.html"><FONT ID="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#FFFFFF" ID="NavBarCell1Rev"> &nbsp;<FONT ID="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="overview-tree.html"><FONT ID="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="deprecated-list.html"><FONT ID="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="index-all.html"><FONT ID="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
-  <TD BGCOLOR="#EEEEFF" ID="NavBarCell1">    <A HREF="help-doc.html"><FONT ID="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
-  </TR>
-</TABLE>
-</TD>
-<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
-</EM>
-</TD>
-</TR>
-
-<TR>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-&nbsp;<A HREF="proxy.html"><B>PREV CLASS</B></A>&nbsp;
-&nbsp;NEXT CLASS</FONT></TD>
-<TD BGCOLOR="white" ID="NavBarCell2"><FONT SIZE="-2">
-  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
-&nbsp;<A HREF="telnet.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
-</TR>
-<TR>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-  SUMMARY: &nbsp;INNER&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
-<TD VALIGN="top" ID="NavBarCell3"><FONT SIZE="-2">
-DETAIL: &nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
-</TR>
-</TABLE>
-<!-- =========== END OF NAVBAR =========== -->
-
-<HR>
-
-</BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/images/bin.gif b/web/root/telnet/Documentation/images/bin.gif
deleted file mode 100644
index 57f865726866125d4318578574a52ebd455943f6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1272
zcmZ?wbh9u|v|+GeSjxa~<Iar<Jsk}#4KLoj*s*6vMMXtMM#hRYD;O9Ue*F2dW5*5!
z1%(wWR&;c97|Z|x0Re%6l7fJcfP|EUgc&mo3=9lX5*%C{7z_*&&YU=L=ERIMCk#?%
zFfa%t%s9co@MFi86%i2;3^W5eAd^9UVPN~`&`{u^Bh`Om$s~ygtN8&b4h8~3i*Do?
z259i?yJqmfN^J7^XC;-ramBkE-XCySfBXH9J$3!|CA&ilSy@_I+uA!iySm#M>RAip
zn|r2Aoz}|SpU~IMF>n5Yg^Lz1=<chZICC!NnzifJZ`inoqkCq3<Ek!>O?&pP-O{zK
zA#`Ww?tLdV9Ozu$FmduRPA+cdH9Wj)8MsasZr<8o=yaiDHy_7s{&@no1%=iy3vu7z
zWDr;*#39VUdAj3J-1)FThXV`;nmG8xLL?F%IyEth8$?7HC^lL$FiQq(xc>fvy}{8F
zswZYJHnA~qKD10}aOhyJekKv-vEd+-eekS9z1AUojsMza35i6kU~p)1E?#4Cz{TLe
z9sj=j0fM(PGp@JQGHaM@Q8?n%GEqXsrQySYMSFRK6+_-+yjXlLbp5`R>gNZVT60ZS
zU)=qQ_hO^=gC_yHTf7gpu3y8Dk+8JEQG0KHtnR*-(+?bXXy*LsqEN`lT){20BsgMQ
z+)Wl?ffI{68Kk3`>SsI7y5;$@Vb(7L2Zn~kLv1sjZRowb;bKzusrG-nre1mZ^zdP6
zv6veL6;JfISr}th>}XuMbJO}{_H~nHGB9z8s>MVwEJ$l!CZOkV;F&{{03WwXNWpiZ
z8l{ihFD&y86WjCsf}KgA17p(!!RHOhCb<{FiyAytIbs%tGCMHy{_|0ruFqW3F-MWX
z%2_Uk*_T0(MYz`Npby(q^P9}?cmf>R7q*5!G!TDvh^tI6$3Q;dR%@FqtA40pe<`np
zL6V{>(~&v-oC)6@*w}q9A7GQ%P=3%y_2TBZsXl=pR1^c-4s%YGiFpt-UGPqs1cTU_
z?eeyzQA+~){>LgF&}9s~dDgi`@RS!@>D+5q!X+#dmi29N-IQh`W%8jRuJ<U@P3w&g
zt2!HwWiU9_80cLHV&d@;Xk5g$NUbsM)?CipeVe~#oN>|6UEol%o1vkBl`kehtc>db
zvm?u!iJI9t^|LHAEjbtNiY{5myM)oXFzNcejaT-z-`%vODMXG*j>lBq_n(Btl!Mdx
z9RdR8#dsZHh~#<uNOM83l(7RFx7ss?u+qeUYx}PL5#u-OZj#nalQ`wnv&_k5S;6L@
zwj;qt68|qfKcxQ3>C|zB=|MAd9>pbnIvH?Ds_&@A_B9-vf7reLbVg(nkJed>#gcgo
zET7L&J1ozl`{j&dywR663;{kqdalA3OiuZSU;1)h);xFXRX^+4FIQV+)|M(d)i2n5
z*0yNx)*Bh=wcoC1FMj*&x<~MyPbz8q-)_5GaoG0Dt=0*DrroP-zgzmCto-b@8!Qb8
zH<ydI{oQgsQ@-xYlcvvmi}%?+-?L%u%*T7?oSD1Z$L#dN#e42B_+~8pCEUnZz#zZj
tz95sL>}KhJ-*2}(&$E_0w@2>x`vdI!?e7nY=kJj_BENpmCr1VbYXC2g5QqQ(

diff --git a/web/root/telnet/Documentation/images/border.gif b/web/root/telnet/Documentation/images/border.gif
deleted file mode 100644
index af596665f7e367c40df12ce77197b5cccb367b76..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 459
zcmZ?wbh9u|v|+Gec+9|X<Iar<Jsk}#4KLoj*s*6vMMXtMM#hRYD}Ma>v17}Mh=>RV
zRG<Umg7h-5{1Z6gxq7cfw$!%&3Su11E-e{RtgeQ8(s<@<_PJjc_x{JZZ4K8CIOs_A
zpIB1VX|pA2X+opJ+Nkw6)|6GV+HfDdF5;*+`}~V7Rldvk_8I6m`mVn%81mlu&%FEp
z8!A}x{R3+oJ4*8kRNFiIGb;-clP63|?XC`-I(^#AfGKrzC(PIDowK-eQmbRp$|cS5
zYx~xBEbU#lwS!%KlhcY_<$GEJW-mH;Xz#3`<0ncFPfj{}E@w}bgTa9-+1Gl`+%CI&
zddq`5rxvK*dYb;|=>6BvuInxQ@Nu8etFIi_nATkSm3gc2q#5r52Z<*PlLRfa<8ABy
z6=~`ee+Y4`Wq;|rq#{pYUw0$F{}<=!0gBE|(w3*3UkKbgS+1$LX!4GYN@r%tN>4Sp
zu~GDFr_uAj-aFT;do406PZF_Q_jFgN@!V<N0;?yuE@}$P^5ML?_)^vOC{NbwQ&p#L
z&bS<<s8-^@E?QuJ$NIpMcY>@gAEYjvHeY{#uZP3Mt=>B}Bq}uXYR9cnF?@WyUzvfy
F8UU)1*!lnf

diff --git a/web/root/telnet/Documentation/images/check.gif b/web/root/telnet/Documentation/images/check.gif
deleted file mode 100644
index 6dca2af7fd56c87de9e9cfcf0dd866fe0506f892..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 230
zcmZ?wbhEHbG+;1b_{abP_4W1t7#RNj`=|Jmg^_`QnL!663X*4F+CHa$<>|Nli|1^)
z)t!9e568vQgWFrLdCa<Ye3j_!U4m!WmY!MgrQQ5@-D}>G{j&-p{&04lT-><w4omEX
zyB}_OAIpiEx$Vf@Rg<<Q{=C7{9c%ycrX@q!b)ywC<MQ{1X8*o-WO2gR^d=+MlJ@C|
zUtE-*rW_Gr%VfFyG$q*Cq;iVqrplYaX&kF09$9*L9KG7s+L^Vo>+ma%b_G#wYi{YR
i?6xC|wP&4tS*;$B^JmJ6n%^0_uH62^;v2}oU=09xd~8er

diff --git a/web/root/telnet/Documentation/images/checkS.gif b/web/root/telnet/Documentation/images/checkS.gif
deleted file mode 100644
index c86ec6c9b9744567cf8d63d73d8263497e975abc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 139
zcmZ?wbhEHbRA7*2_{abP_4W1t7#RNj`=|Jmg^_`QnL!663X*4FikZ{D^7LE&#WhJ$
zfx1T`t?yNGb#!u{oXO6$DrY6%dx0xT**Da@URS>^UfN{O7brA|x4t9MA%j6yf9cX!
okw<;hO@(x?Dd*}&C|ihcn0Gqa_j|RS&Fy)&jQe(Eb1_&00ES#LM*si-

diff --git a/web/root/telnet/Documentation/images/doc.gif b/web/root/telnet/Documentation/images/doc.gif
deleted file mode 100644
index 238fdb14e1c5956f0d2a8adad207147bed7509f1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 922
zcmZ?wbh9u|v|+GeXklQuap%T_o{ol=h8J&M?AWuTqM{-rBV)yy6$}gv41XAY{CTip
z!-fl6I)425@q>Y3gFylV!;c+XRzyTZfD{mcbwFxBb}_L2Q&8wj$()z5YSO`1`wXNw
znmvwG#Id;={@JGRz)VD_|5!?4XUwOnhWiH`)<&(rv8Jrs_Dj{)j7A5&+2>zusj6kG
z;l6l(##y%;@4x(cz+3YCav5h+b3=VfUVLG7TY7UJCr4WO#QN&^#O|IHj{d&M99&#w
zQ)^6TPHLFkTt08%x<&OJ35CTgCY3FmvwG*+h1?65bS7?1t(d)jSJQ$;ecb2H9jL0=
zvVFni^=DS^KRMyXhLULs%Z`-aJF$Dui+zixWN)$ytUJnc`o@LHyu2?@ah+fHHtWzq
z$?!FIv@@Pbv0VDbeS3u|lkZ98h6cIjy7N-&te2mg>ei6Pcc)@;#<?Zk_x=f3g-jQm
zEj3ZFJ$IGI=LN^6-Q1OE^8IbkiSmy|mOGr!Pd`;Bvs>1beG<c&%%T%KTZ2uO9xGWe
z?Ng1%Gj&getuh81BKRL)ox|}(()9brx!1i}mR>rt^<wm<Ev3D`PKy>TF)&PRyR`bH
z;KOa9zj(4!uYaHGQk2ZDAG~M%n@2H|cK;G(w<vJkG;MO*y<a~c-i{2kbyr(cbbIT`
zCC7_wZ39>gH-;47zMb`b$(w_GbM6TgbVfZ`x&Nh->fipXCkIzIU+yyw)XIC2I#F`X
z_sDI<H~E(2e-pfZrnFll{M{kb=_|f}NL@BP{O;Z?y}UnmFWm*~D*ik?dez~K&C<}R
z(dxgx{#(AF-maJZh~~44F4HttD_U^Y)L%&BYWcGD`9gL6mLClDJtxwmr5^sBHmAMJ
zLP059lBq+geUi;|h31EM8y6~m;@r`e;1alXajEmC7Y}OEpJ+%jyJmg5RN=@wL#4>q
z>Zc0Bk%eKhT|Qx%sZvr(ol4E|&lWB$OkH+UA;3tI>EP^?J)DeE1zDehr+rxR?AaWa
zE0t~Y-8Q{cRxbUenLfW*F6-IExo<Vq=NG7HWzO$T`YGO<Bf|A^;Y78pj^vLXGhZ$%
z{M7kk<+62xjEsh+v)UJJDC^2tRno9(`K(NaS?sgdJ^HnLU5<;$s`W>Vx-vKVas77Y
zwlevBD4ap&z|k$?N*)faTiFs;zukH#t^26Xr6BEhJDxZR>g{~7Z1vHdueROJ<6y7`
E0P48BeE<Le

diff --git a/web/root/telnet/Documentation/images/flash.gif b/web/root/telnet/Documentation/images/flash.gif
deleted file mode 100644
index 1942939e04fa4afb5e6b9298d323f0b74fc5e7a9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 167
zcmZ?wbh9u|RA5kGSjYeZ|3H9&f#IJHhzAm9U}|XTU)lZ8Zqb}AeeKP6ZJxy$9%pTw
zthi(qSH&#Jg|D+J3V6<xRC{mITk*Bvz?6a?mu0I@-u!g!4sWMxn`Tr>?B)Z49tUgV
zibFq6zw=b_qL=C9y~WNCbu2gA*vh{8_VWCjcD5a9CO^Vvq$?(8d}?|$)k8@5iuqRq
P21(DNm6jg^7#OSp-*!qc

diff --git a/web/root/telnet/Documentation/images/left.gif b/web/root/telnet/Documentation/images/left.gif
deleted file mode 100644
index ba9bb0addc873a50162a66f16f964d7b94b36177..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 497
zcmZ?wbh9u|lw*)%_{_lY`t7^B4<Dbua{bWBvs?EbTDN`A@(tT&E?M0(V?kZd)bh6e
zoT}!8oRZLlbpNOX1_lNl1_lNOkX{Cse*z~wvqcXvEvx2jOf5KIz;PszN1;tnM}WsA
z@xY-8I+rXZIu2<zC|%)bnwFUv@`1rEO60Tfyh$!GeA7HU_}G;X7Tk`TuHklYs*i@s
zlO~y-DYJUoTo)&%vI^v@+A$aTu)48w^6~NV7<jli*ybcBC2>?&^Yod7u*Wr}w@;th
z%frnb7Q(?{s}>!`%G0xqo6C>gyM=?@*1&|#iIcZy`3lZ8T#5Txcp3wP4p;YV<BH{o
zZFjPqEmUN{zIo=A>o@Y8_A_&<v@^4J&*JI5w&odUf6`rsoXV}+Voz+?D74u{equP2
zK#rBz1kp__i5KpCy6Sg^G35U%%QI6CvuNipUo%b7apD6JhjwlyrA@5Q8T57a?REwp
z$aWWK`jyeJb5e8H?qoe-mfvazdRt_QCgl_cE^au`Xx74_aQW~t4{J3ZnGH#Pw`Z-~
z`z_Pf&~3G-RS1tpMDXL5LyM!bGGr~kAKj+o>+$7{NAm0jC)1E0GZUGw28!Fn-_*4J
zv_P=7nm3j)#=(H&P-VrF#vdW9k~6L6wEk%keAMR4TRHLPom52$BQEh3eGClN0Q8T<
A`~Uy|

diff --git a/web/root/telnet/Documentation/images/mail.gif b/web/root/telnet/Documentation/images/mail.gif
deleted file mode 100644
index 864ed206d575baa9e780086cf7f35ecd69d00624..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1069
zcmZ?wbh9u|v|+GeXklQuap%T_o{ol=h8J&M?AWuTqM{-rBV)yy6*sm_n9)+PW6O#g
zM>hPpbKu974L{Dzh=_==u(0^?=SPOc4F-lE3`77OkQ$I(46OeY6#7y!=Vh##bnw+a
z11XMXk0TXvY_5iXwkbR?6A|h^mQvUm^Qo%g{sD)zQR{E4DeJcVQnfXs(Lrza`4?NN
zYT0VIFW#TA*G+|~y}9X}I?p|ayxL4|&bIb8cE;-F)V%n@`rdRN9&RqqDi*HF4#t^1
z+4b>>lP0I~PUYq0o5{(=705EZV`evZ`r-uBWy@K3RvuW*$5F<~R$ZvX6~fQSRFTr3
zP*}Wg^^G-a_;`<UvT~l|ns%I@KZG%5S3%;n<$NDL++01CXYKLO4gvm)ulMYI|19xw
z8?S^-^3gYMUbA;h-|hL~+uLe|i*t51vUlq`Z1`}1*CCLlRz+apl9Q9w|FIv~Y;iHh
zHTUC=ldoSq_w@)Cyd&Xo@^Xq#(?>S0GtU~9?VnIwBxJ$Rm~wuyVSDQ8^!(XAxhBgu
zPIg}Qgu#f7$>yu1h!Zns#^qJM+-DYCY*U&vOGWqrW5Y)K#G=*eCF@tMF_SIx^<+EL
zxI9eOreMQ1!&?cf?@d}|CZRf$iH+?;&#%c30`~DTI5R0te=e}h&sVMOz>SYls}}yO
zyu!uu{PKF1ACA6ru73{Mw`d`U-`|oBMvijj4O|EIR6b7#X*~VUVyD~7M;DkJ7985W
z+2+N?WcS6kHE$T+vS%$Q`k;Am@3S+}o7b8!L=?zR>6|TdU<%v$clR8{=GI=$6rJR;
zO!8mC_24@k1^v~TTbb@Ru=J@rly0k3ZAg5^_(Ha|UQ}X6=~P42YZ0ot-+QXt96tLn
z@ET0#s(5skP27*c<^gk89Pi`UkQ&~K`@Yv~7R0w3vR#m>X1eoGuE*%ojH|6zCU`KI
za5*h_IxVJ1^J@QnPG&jg?^n&*m6$X#F3!raGvJ@W$KgDo<Z<H>QOT|Y%b(Btyy4lc
zs0&v;<`xvrop^j<hg#Kv*$JmC(^Z3$egsI$>9#&!+I{ZEfu#CXqUq7jlb&%)=1P81
zcI)TznkTVX$YAke`Ck(01`9V?z0%^&nQ~#>o^M?Tm?nm4PSWp6x%qVCiL6S$xl4GS
zb1P?UvOLI=yLDS8gNd1!*YPcP{2GqUJL4<eu*0}vcDqTV=&rTfSvH(@UwT<<)(Yj2
zN9F3$muEeEyYJnz+j{$(B{W{``dc<z{{Z`L@8hc&RzB31WdF}ycu43wqr+a8PY?AE
z32pA)a46?Oo5;aCWq;NmRJHeMw?DV-m64Rv{okL?yy#2QK5HVMs(Q}Y*hcgGz85v~
z_*@Q8c*hu{QhU^r%j|K<<pATeTSTuYto?E|BAvJOYGnD_tzt3ty4!9jGB8*J078-B
AP5=M^

diff --git a/web/root/telnet/Documentation/images/star.gif b/web/root/telnet/Documentation/images/star.gif
deleted file mode 100644
index b079be2c501f965e2afd106c2d979c43938a6616..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 227
zcmZ?wbhEHb6k!lz_`(1J^LkxAUyr!7H|Y80{P)))zFmvhz1nZzdcTji(iTj0J9T8?
zgVSNp&xe1!8oqn`oPDcejvQL^=5oP~CBC0;Ccio#v20ez)&0S5E{B86!oZ3@Sr|cr
zIv@gMCj)DOf<j+P<~)a%bxMa938d|LcJPczrgVs&oV83>fQDEDZ?oe85f6`khocpH
zOlLN`Y95#?B7L?|;;3%?k&LiN!2^78F|19uTTY+OJ?LGiZ~aAEtcXLogj2gYI#;_T
JMOBf(8UO^^Sy2E0

diff --git a/web/root/telnet/Documentation/images/testit.gif b/web/root/telnet/Documentation/images/testit.gif
deleted file mode 100644
index 51474b21d704de2a8421db05df59a94eb48a36f8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 727
zcmZ?wbh9u|v|+Gec+9|X<Iar<Jsk}#4KLoj*s*6vMMXtMM#hRYD;O9Ue*F3IgMp!<
zrh<Xt$Br#4A|fIf7#MUI7#J8pdKp;$37qg;z1JdJYTJJWF^*=JmW(J?SHnGNJaabt
z+%JoJ|Kr@YhU*6$bfo%EEGg==*%Gxhq0wP&)cPB1%BopyxDQ?zanzfA{>7Fm-(`IJ
z4D=go{Vu%ETJ>GB?z|vpV?}0Jb4Ff)P+e+kN^fIRQ)$nX@(zE4?!GBya~tNjF6^5)
zzoe=&seZxgrSp1cPg;{R-61f!VZ!Xy>vlDAZCszdMK5uC&cQ5BuKjJfEcr83XPs?1
zz5m2!^%X@|T25WtIPHAAW6-L*J=Y%G$UJ=M&WoP=uTD3!->Pvs_VM$VujSttIBxvP
zF0?!4-!)mC;(UMI;;AaGkFIJyT+b>YuEf!i6{Q#UZ;_2E&y>K&ZZ%uIB&t=iuB+E`
z8t8FsU#2LNxxJzE7ALpB-WkTeMlPIe&n>h5He6bgEF8SE_IT69(>K<>bm*v<VjCo0
ztm&}m(z1&`#kgdyu5vkjaQEZxnY%V;Fme>k$_lt0pVqo+V^qk~i>9&D&mNZO=eh3_
zz?l4J=YkCnx46C4tGj)p@bJ!ndH+K1<S%<XnJ?C5+MWY%(l5vg%g^4~^C5j-R;ae#
z-LGF#cO`8;SMpbCQ~VO6uSTrayCUCSUhz56RqlP+v-@Xe3&+dNz4b6DW>a?J0f*nB
zH&`|n$lssqU8!~FVz}q}qm~;(9&s+QpWpgd^!K-o4c~Xq{a5kj?%zK41vPJ;e!p8%
z_hoxr0}J1dgl4|9hjEsi*UIH8S=e68Z@U*Nc*{CiLn5*H=h<?_+HeyCKa*z>9rHV%
z?tQVKF`}j;w%y`chHHjM+lt5i_c*`IlW`C`d9>Y4a;1uxQ`^->u7ElRKTdXo#HWIM
p0uz@qMwhL8Iz2{dV&v5DX_?PvI!vg1HY-PKVcP71Lz0XP)&L+6V~79%

diff --git a/web/root/telnet/Documentation/images/user.gif b/web/root/telnet/Documentation/images/user.gif
deleted file mode 100644
index 12608388b86a9fdeaa4d92eb5b40606483f3604c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 788
zcmZ?wbh9u|v|+Gec+9|X<Iar<Jsk}#4KLoj*s*6vMMXtMM#hRYD=HdxFfcIu_yZy$
zA|e=IKnKJC>1AN~Cvd`Z^<Il?scrui#5kH=S~8+oT@Cl7@yyxmbH6O^{f~3o8m=F3
z(2?ptv81TeW=qu4ghq$8QR{E4DXV6+;XZhMinreE)y?{6s`Ga5zF%;wYen#D4yL&E
z$KU@g3$L!N49WBlEH3G8tu61aDen!PJh?qUwQEYxgvqn#Pv{Sg>qtyqFtKsLyqKwB
z(-Q)hFWS=F(#W}U*KW=&6Wg+uH7%U9m1QT_$y2BH?mxG|VAj6Mb>~i;KE3mD*pdLp
zoRZeE#wVfIZ=Ae!v@fG@=H^LHo2#F{cr|6)fu*V+^1t5rzC*_1q2C#4?f;uhwT%U4
zt!WMsGKxMi;lbqmOSe3H+&3zjB>wv+v|#p$)ymTv?<RfZw%n9zWu$R+&*$ewOY~-l
zHJ&-*{&w2jNv6erCDT=(*%j<R)p%>g1GV{Xt2iW%s<2Moq2t5+&BgR8>xt6UN?kdU
z2BO+4Et5SP(ogqCY`V2da92~%fqgx%)pGZ!i|gE)k~kskGWYZwVezqx=PH$7oufCY
z=}6$@7!%!NM~iFsJ$<2b?`lo`+p6f5g*m~K1%%(56ra^}*_CPh@aiS0udnyNyS7O#
z`diu5UFE+M9iQDY&pBYw5-)i3_K){BR&N(6ud~^%=J1#yapv^B>fhWBxGR@ln=$v{
zlBe}6*L9tGRpvMU+#^0YF0FmvKib}`?~D1~^q*Vd^oRKk3_=_am{?>w9>}sw-B{54
zurf8EiBZi%QIz{%yJ8z-)`|9Z(JYGxt+A|T$2vHeBs$wed0r&SDIA)S)W)%HIY*aF
zn8(8=OT7&D9_wE#5?iJhMz}Y)FX~9?*5or<(BP)zsnDjG%EZ?p7PmsRH>zmI!&ZfJ
zF9fQszuj2ek?|=aZ6=po#MAIAKd;@-Dctu_FthYtSZYb7T*-3=*%b;e*muevII_^)
qz@cRko58CWizb+LwV1alWxZTFgDFsR*_^UfN0!ZNlgeUYum%8vO?wLf

diff --git a/web/root/telnet/Documentation/index.html b/web/root/telnet/Documentation/index.html
deleted file mode 100644
index e2c861089f..0000000000
--- a/web/root/telnet/Documentation/index.html
+++ /dev/null
@@ -1,696 +0,0 @@
-<!doctype HTML public "-//W30//DTD W3 HTML 3.0//EN">
-<HTML>
-  <HEAD><TITLE>The Java(tm) Telnet Applet: Documentation</TITLE></HEAD>
-  <BODY BGCOLOR="#ffffff">
-    <A NAME="TOP"></A>
-    <H1>The <A HREF="http://java.sun.com/">Java<SUP>(tm)</SUP></A>
-      Telnet Applet: Documentation</H1>
-    &copy; 1996-98 <A HREF="mailto:leo@mud.de">Matthias L. Jugel</A>,
-    <A HREF="mailto:marcus@mud.de">Marcus Mei&szlig;ner</A> 
-    <HR>
-    <P> 
-      The package contains several parts of which the most important one is the
-      <B>Telnet Applet/Application</B>. Select from the list below what you
-      are interested in. If you only want to use the applet choose <A
-	HREF="#SetupTelnet">Telnet</A> from <B>Setup</B> and if you want to use the
-      <A HREF="Source/packages.html">packages</A> in your own programming, select the
-      appropriate from <B>Source Code</B>.
-    <H3 ALIGN=CENTER><A HREF="#Readme">READ THIS FIRST</A></H3>
-    <H3 ALIGN=CENTER><A HREF="#SetupDoc">Setup</A></H3>
-    <P ALIGN=CENTER>
-      <B>[
-	<A HREF="#SetupTelnet">Telnet</A> |
-	<A HREF="#SetupTerminal">Terminal Emulation</A> |
-	<A HREF="#SetupModules">Modules</A> 
-	]<BR>[
-	<A HREF="#SetupAppWrapper">Applet Wrapper</A> |
-	<A HREF="#SetupProxy">Proxy Server</A>
-	]</B>
-    <H3 ALIGN=CENTER><A HREF="#SourceDoc">Source Code</A></H3>
-    <P ALIGN=CENTER>
-      <B>[
-	<A HREF="Source/telnet.html">Telnet</A> |
-	<A HREF="#SourceTerminal">Terminal Emulation</A> |
-	<A HREF="#SourceModules">Modules</A>
-	]<BR>[
-	<A HREF="Source/appWrapper.html">Applet Wrapper</A> |
-	<A HREF="Source/proxy.html">Proxy Server</A>
-	]<BR>[
-	<A HREF="Source/packages.html">Packages</A> |
-	<A HREF="Source/AllNames.html">Field and Method Index</A> |
-	<A HREF="Source/tree.html">Class Tree</A>
-	]</B>
-    <P>
-      <A HREF="../index.html">
-	<IMG SRC="images/left.gif" BORDER=0 ALT="[BACK]"></A>
-      <B>Get the <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">latest
-	  version</A> here!</B>
-    <HR>
-
-    <A NAME="Readme"></A>
-    <H1>READ THIS FIRST</H1>
-
-    We found that some people have no knowlegde whatsoever of java and its
-    restrictions. We have compiled a few questions and answers here as well
-    as some reasons why you should or should not use 
-    <B>The Java<SUP>(tm)</SUP> Telnet Applet</B>:
-
-    <DL>
-      <DT><B>Some web page told me I need telnet, is this it?</B>
-      <DD>Yes and No! The Applet is a fully featured Telnet and Terminal
-	  emulator, but usually you're better off using the program that
-	  comes with your system. Most of the UNIX based systems have very
-	  good terminal emulators (xterm) and alway have a telnet 
-	  application. Windows 95 comes with a telnet if you have the network
-	  stuff installed it's there: c:\windows\telnet.exe. It should be
-	  sufficient. If you want better terminal emulation and <I>colours</I>
-	  the better choice is <B>The Java<SUP>(tm)</SUP> Telnet Applet</B>!
-	  <P>
-      <DT><B>I cannot connect to some.where.com? It only says 
-          &quot;Trying some.where.com ...&quot;</B>
-      <DD>A Java applet is restricted in several ways. One of the restrictions
-	  is that it may <I>only</I> connect to the web server where it was
-	  downloaded from! So if you put the applet on <I>www.where.com</I> but
-	  set the &quot;address&quot; field to <I>some.where.com</I> you 
-	  won't get a connection. Read about our 
-	  <A HREF="#SetupProxy">relayd daemons</A>!
-          <P>
-      <DT><B>But I loaded the HTML file from my harddisk and it still does
-             not work!</B>
-      <DD>Netscape and Internet Explorer do not accept your hard disk as 
-          secure space. So they will prevent the applet from accessing any
-	  resource, such as the network. You may overcome that by adding the
-          directory where the applet is stored to your &quot;CLASSPATH&quot;
-	  environment variable <I>before</I> running the browser.
-	  <P>
-    </DL>
-    <HR>
-    <A NAME="SetupDoc"></A>
-    <H1>Setup Documentation</H1>
-    <A NAME="SetupTelnet"></A>
-    <H2>How to setup the Telnet Applet</H2>
-    Make sure, you got the
-    <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">latest version</A>
-    of the Java<SUP>(tm)</SUP> Telnet Applet. Refer to the
-    <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet/index.download.html">
-      download page</A> on how to get it and how to
-    extract the files from the archive. After successfully extracting the complete
-    package you should have a directory <B>Telnet/</B> containing <B>*.html</B>,
-    <B>*.java</B> and <B>*.class</B> files as well as the directores
-    <B>Documentation/</B>, <B>display/</B>, <B>modules/</B>, <B>socket/</B> and
-    <B>tools</B>.<P>
-      To install the applet on your web page you need as least the following files
-      and directories. Make sure that all files and directories are <B>readable
-	by other users</B>!
-    <PRE>
-  index.test.html
-  telnet.class
-  appWrapper.class
-  display/
-  display/SoftFont.class
-  display/CharDisplay.class
-  display/Terminal.class
-  display/TerminalHost.class
-  display/vt320.class
-  socket/
-  socket/TelnetIO.class
-  socket/StatusPeer.class
-  modules/
-  modules/Module.class
-  modules/Script.class
-  modules/ButtonBar.class
-  modules/MudConnector.class
-    </PRE>
-    Now edit <B>index.test.html</B> to adapt it to your needs or look at the
-    example below! The file is documented and if you have questions about
-    the <I>Modules</I> refer to the
-    <A HREF="#SetupModules">Module Documentation</A> or look at the <A
-      HREF="Source/Package-modules.html">Source Code</A>. You will find, that not
-    <B>telnet.class</B> is loaded as applet, but <B>appWrapper.class</B> instead.
-    This is necessary to enable the <B>detach</B> feature!
-    <P>
-    <DL>
-      <DT><B><I>Important Note:</I></B>
-      <DD>We would appreciate to see <B>credits</B> on a page using the applet
-	which includes a link to the <I>
-	  <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">applets home
-	    page</A></I> and names of the <I>authors</I> as mentioned on
-	<A HREF="#TOP">top</A> of this page. You may simply use our
-	<A HREF="../index.test.html">test page</A> and edit it to your needs.<BR>
-	In response we will include a link to your page on our
-	<I><A HREF="user.html">user page</A></I>, if you like.
-    </DL>
-    <P>
-      The telnet applet can be customized using the following parameters:
-    <PRE>
-    &lt;PARAM NAME=address    VALUE="tanis.first.gmd.de"&gt;
-    &lt;PARAM NAME=port       VALUE="23"&gt;
-    &lt;PARAM NAME=emulation  VALUE="vt320"&gt;
-
-    &lt;PARAM NAME=proxy      VALUE="www.first.gmd.de"&gt;
-    &lt;PARAM NAME=proxyport  VALUE="31415"&gt;
-    </PRE>
-    The <B><I>proxy</I></B> and <B><I>proxyport</I></B> parameters may be left
-    out. They are needed if your target host is NOT the same as your web server
-    and you are using the <A HREF="#mrelayd">relay daemon</A>.
-    <P>
-      <A NAME="Example_1"></A>
-    <H3>Example:</H3>
-    (all possible parameters)
-    <PRE>
-    &lt;APPLET CODE="<A HREF="Source/appWrapper.html">appWrapper.class</A>" WIDTH=600 HEIGHT=480&gt;
-
-    &lt;!-- appWrapper parameters --&gt;
-    &lt;PARAM NAME="applet"      VALUE="<A HREF="Source/telnet.html">telnet</A>"&gt;
-
-    &lt;!-- optional (WIDTH and HEIGHT should be changed!) --&gt;
-    &lt;PARAM NAME="startButton" VALUE="Connect to www.first.gmd.de!"&gt;
-    &lt;PARAM NAME="stopButton" VALUE="Shutdown telnet!"&gt;
-    &lt;PARAM NAME="frameTitle" VALUE="The Java Telnet Applet: WWW"&gt;
-
-    &lt;!-- applet parameters: address and port and emulation --&gt;
-    &lt;PARAM NAME="address"     VALUE="www.first.gmd.de"&gt;
-    &lt;PARAM NAME="port"        VALUE="23"&gt;
-    &lt;PARAM NAME="emulation"   VALUE="vt320"&gt;
-
-    &lt;!-- terminal emulation parameters (optional)--&gt;
-    &lt;PARAM NAME="VTscrollbar" VALUE="true"&gt;
-    &lt;PARAM NAME="VTresize"    VALUE="font"&gt;
-    &lt;PARAM NAME="VTfont"      VALUE="Courier"&gt;
-    &lt;PARAM NAME="VTfontsize"  VALUE="13"&gt;
-    &lt;PARAM NAME="VTid"        VALUE="vt220"&gt;
-    &lt;PARAM NAME="VTcharset"   VALUE="ibm"&gt;
-
-    &lt;!-- module parameters: #1 is a buttonbar (optional) --&gt;
-    &lt;PARAM NAME="module#1"    VALUE="<A HREF="#ButtonBar">ButtonBar</A>@North"&gt;
-    &lt;PARAM NAME="1#Button"    VALUE="connect|\$connect()"&gt;
-    &lt;PARAM NAME="2#Button"    VALUE="disconnect|\$disconnect()"&gt;
-    &lt;PARAM NAME="3#Button"    VALUE="Detach/Delete Window|\$detach()"&gt;
-    &lt;PARAM NAME="4#Button"    VALUE="Send:|\@send@\r\n"&gt;
-    &lt;PARAM NAME="5#Input"     VALUE="send#10|who"&gt;
-
-    &lt;!-- module parameter: #2 is a scripting module (optional) --&gt;
-    &lt;PARAM NAME="module#2"    VALUE="<A HREF="#Script">Script</A>"&gt;
-    &lt;PARAM NAME="script"      VALUE="login:|leo"&gt;
-
-    &lt;!-- make sure, non-java-capable browser get a message: --&gt;
-    &lt;B&gt;
-    Your Browser seems to have no <A HREF="http://java.sun.com/">Java</A>
-    support. Please get a new browser or enable Java to see this applet!
-    &lt;/B&gt;
-    &lt;/APPLET&gt;
-    </PRE>
-    <P>
-    <HR>
-    <A NAME="SetupTerminal"></A>
-    <H2>Setting up the Terminal Emulation</H2>
-    The Terminal Emulation is a very important part of the Telnet Applet, because
-    it enables you to use programs that make use of certain features of hardware
-    terminals like <B>VT100</B> or <B>ANSI</B>. Supplied with the package is an
-    almost <B>VT320 compliant</B> terminal emulation, that should include the two
-    mentioned earlier. This means that the applet can do colors, even if the
-    original <B>VT320</B> terminal cannot!<P>
-      The applet supports the <B><I>special graphical character set</I></B> of VT
-      terminals. The new implementation supports all graphical characters with a
-      small drawback. The more graphical characters on the screen the slower is the
-      display. We will remove the current implementation when full UNICODE support
-      is available from all browsers (full JDK 1.2 compatibility).
-    <P>
-      To configure the terminal emulation look at the list of parameters below:<BR>
-      <I>Note:</I> Default values are typeset in <I>italics</I> and other possible
-      values in <B>bold</B>.
-    <DL>
-      <DT><TT><B>&lt;PARAM NAME="localecho" VALUE="<I>auto</I>"&gt;</B></TT>
-      <DD>Sets the mode the local echo should be handled. If using auto,
-        or if this parameter is not present, the applet autodetects localecho
-        mode using telnet option negotiation. If set to <I>no</I>, nothing
-        will be echoed, ever. Any other value enables every character to be 
-	echoed.
-      <DT><TT><B>&lt;PARAM NAME="VTcolumns" VALUE="<I>80</I>"&gt;</B></TT>
-      <DD>Sets the columns of the terminal initially. If the parameter
-	VTresize is set to <B>screen</B> this may change, else it is fixed.
-      <DT><B><TT>&lt;PARAM NAME="VTrows" VALUE="<I>24</I>"&gt;</TT></B>
-      <DD>Sets the rows of the terminal initially. If the parameter
-	value of VTresize <B>screen</B> this may change!
-      <DT><B><TT>&lt;PARAM NAME="VTfont" VALUE="<I>Courier</I>"&gt;</TT></B>
-      <DD>Sets the font to be used for the terminal. It is recommended to
-	use <I>Courier</I> or at least a fixed width font.
-      <DT><B><TT>&lt;PARAM NAME="VTfontsize" VALUE="<I>14</I>"&gt;</TT></B>
-      <DD>Sets the font size for the terminal. If the parameter 
-	value of VTresize is set to <B>font</B> this may change!
-      <DT><B><TT>&lt;PARAM NAME="VTresize" VALUE="<I>font</I>"&gt;</TT></B>
-      <DD>This parameter determines what the terminal should do if the window
-	is resized. The default setting <I><B>font</B></I> will result in 
-	resizing the font until is matches the window best. Other possible 
-	values are <B>none</B> or <B>screen</B>. <B>none</B> will let nothing
-	happen and <B>screen</B> will let the display try to change the
-	amount of rows and columns to match the window best.
-      <DT><B><TT>&lt;PARAM NAME="VTscrollbar" VALUE="<I>false</I>"&gt;</TT></B>
-      <DD>Setting this parameter to <B>true</B> will add a scrollbar west to
-	the terminal. Other possible values include <B>left</B> to put the
-	scrollbar on the left side of the terminal and <B>right</B> to put it
-	explicitely to the right side.
-      <DT><B><TT>&lt;PARAM NAME="VTid" VALUE="<I>vt320</I>"&gt;</TT></B>
-      <DD>This parameter will override the terminal id <I>vt320</I>. It may
-	be used to determine special terminal abilities of VT Terminals.
-      <DT><B><TT>&lt;PARAM NAME="VTbuffer" VALUE="<I>xx</I>"&gt;</TT></B>
-      <DD>Initially this parameter is the same as the VTrows parameter. It
-	cannot be less than the amount of rows on the display. It determines
-	the available scrollback buffer.
-      <DT><B><TT>&lt;PARAM NAME="VTcharset" VALUE="<I>none</I>"&gt</TT></B>
-      <DD>Setting this parameter to <B>ibm</B> will enable mapping of ibm
-	characters (as used in PC BBS systems) to UNICODE characters. Note 
-	that those special characters probably won't show on UNIX systems
-	due to lack in X11 UNICODE support.
-      <DT><B><TT>&lt;PARAM NAME="VTvms" VALUE="<I>false</I>"&gt</TT></B>
-      <DD>Setting this parameter to <B>true</B> will change the Backspace key
-        into a delete key, cause the numeric keypad keys to emit VT100
-        codes when Ctrl is pressed, and make other VMS-important keyboard
-        definitions.
-      <DT><B><TT>&lt;PARAM NAME="F<I>nr</I>" VALUE="<I>string</I>"&gt</TT></B>
-      <DD>Function keys from <I>F1</I> to <I>F20</I> are programmable. You can
-        install any possible string including special characters, such as 
-        <TABLE BORDER>
-	<TR><TD><TT>\e</TT></TD><TD>Escape</TD><TD></TD>
-	    <TD><TT>\b</TT></TD><TD>Backspace</TD><TD></TD>
-	    <TD><TT>\n</TT></TD><TD>Newline</TD><TD></TD>
-	    <TD><TT>\r</TT></TD><TD>Return</TD>
-	    <TD><TT>\xxxx</TT></TD><TD>Character xxxx (decimal)</TD>
-        </TABLE>
-    </DL>
-    <P>
-      Please look at the <A HREF="#Example_1">example above</A>.
-    <P>
-    <HR>
-    <A NAME="SetupModules"></A>
-    <H2>Setting up Modules</H2>
-    Another feature of the Java<SUP>(tm)</SUP> Telnet Applet is the ability to
-    dynamically load <B>modules</B>. A module is a java class that is loaded
-    after the applet has been initialized and may be used to <I>enhance</I> the
-    user interface or to background work in some way.<P>
-      To load a module a special parameter has to be added to the applet PARAM tags:
-    <P>
-      <TT><B>&lt;PARAM NAME=module#<I>number</I> VALUE="<I>modulename</I>@<I>direction</I>"&gt;</B>
-      </TT>
-    <UL>
-      <LI><B><I>number</I></B> is a sequence number, used by the applet to
-	determine the modules. Numbers must be adjacent or modules may not
-	be loaded.
-      <LI><B><I>modulename</I></B> is the name of the modules to be
-	loaded. Modules already in the package are described below.
-      <LI><B>@<I>direction</I></B> is the position of the applet in relation to
-	the window. Possible values are: <B>North, South, East, West</B>. The
-	module will then be placed accordingly. It is <I>not</I> possible to
-	place two modules at the same position! <I>The positional parameter may
-	  be left out and the module will then be placed <B>North</B></I>.
-    </UL>
-    <P>
-
-      At the moment the package features three modules:
-    <DL>
-      <DT><A HREF="#ButtonBar"><B>ButtonBar</B></A>
-      <DD>The ButtonBar is a modules to enhance the user interface. Using
-	PARAM tags <B>buttons</B> and <B>input fields</B> can be added to
-	send text to the remote host or to <B>detach</B> the applet.<P>
-      <DT><A HREF="#Script"><B>Script</B></A>
-      <DD>Sometimes it is useful to have simple script abilities. This module
-	<B>executes a script</B> based on text received from the remote host.
-	<P>
-      <DT><A HREF="#MudConnector"><B>MudConnector</B></A>
-      <DD>This module is a special program for the <A
-	  HREF="http://www.mudconnect.com">Mud Connector</A> by Andrew Cowan. It
-	loads a list of muds and displays information like host and port.
-    </DL>
-    <P>
-      <A NAME="ButtonBar"></A>
-    <H3>The ButtonBar</H3>
-    The ButtonBar may be used to add <A HREF="#buttons">buttons</A> to the applet
-    that execute functions or simply send a specified text to the remote host.
-    In addition it is possible to specify <A HREF="#fields">input
-      fields</A> as external input means.<P>
-      To load the module include the following tag into the <B>.html</B> file
-      (example):<P>
-      <TT><B>&lt;PARAM NAME=module#<I>1</I> VALUE="<I>ButtonBar</I>"&gt;</B>
-      </TT><P>
-      Below is a description of possible PARAM tags and a description of supported
-      functions:
-    <DL>
-      <DT><A NAME="buttons"></A><B>Buttons:</B>
-      <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Button VALUE=&quot;<B><I>buttontext</I></B>|<B><I>buttonaction</I></B>&quot;&gt;</TT>
-      <DD><B><I>number</I></B> is the sequence number and determines the place
-	of the button on the row.
-	<P>
-      <DD><B><I>buttontext</I></B> is a string displayed on the button.
-	<P>
-      <DD><A NAME="buttonaction"><B><I>buttonaction</I></B></A> may be one
-	of the following functions or strings<BR>
-	<FONT SIZE=-1>(<I>Note:</I> the backslash character
-	  in front of the dollar sign is mandatory!)</FONT>
-	<UL>
-	  <LI><TT><I>simple text</I></TT>
-	    to be sent to the remote host. Newline and/or carriage return
-	    characters may be added in C syntax <B>\n</B> and <B>\r</B>.
-	    To support unimplemented function keys the <B>\e</B> escape
-	    character may be useful. The <B>\b</B> backspace character is
-	    also supported.
-	    The text may contain <A HREF="#fieldreference"><B><I>field 
-		  reference(s)</I></B></A>.<BR>
-	    <P>
-	  <LI><TT>\$connect(<B><I>host</I></B>[,<B><I>port</I></B>])</TT> 
-	    tries to initiate a connection to the <B><I>host</I></B>
-	    at the <B><I>port</I></B>, if given. The standard port is
-	    23. <B><I>host</I></B> and <B><I>port</I></B> may be hostname
-	    and number or <A HREF="#fieldreference"><B><I>field
-		  reference(s)</I></B></A>. If a connection already exists
-	    nothing will happen.<BR>
-	    <FONT SIZE=-1>(<I>Note:</I> It is not allowed to have
-	      spaces anywhere inside the parenthesis!)</FONT>
-	    <P>
-	  <LI><TT>\$disconnect()</TT>
-	    terminates the current connection, but if there was no
-	    connection nothing will happen.
-	    <P>
-	  <LI><A NAME="detach"><TT>\$detach()</TT></A>
-	    detaches the applet from the web browser window and
-	    creates a new frame externally. This may be used to allow
-	    users to use the applet while browsing the web with the
-	    same browser window.<BR>
-	    <FONT SIZE=-1>(<I>Note:</I> You need to load the applet via the
-	      <A HREF="#SetupAppWrapper">Applet Wrapper</A> or
-	      it will not work properly!)</FONT>
-	</UL>
-	<P>
-      <DD><A NAME="Example_2"><B>Examples:</B></A><BR>
-	<FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
-	  examples for <A HREF="#Example_3">input fields</A> below.)</FONT>
-	<PRE>
-       &lt;PARAM NAME=1#Button VALUE="HELP!|help\r\n"&gt;
-       &lt;PARAM NAME=2#Button VALUE="HELP:|help \@help@\r\n"&gt;
-       &lt;PARAM NAME=4#Button VALUE="simple|\$connect(localhost)"&gt;
-       &lt;PARAM NAME=5#Button VALUE="complete|\$connect(www,4711)"&gt;
-       &lt;PARAM NAME=6#Button VALUE="connect|\$connect(\@address@)"&gt;
-       &lt;PARAM NAME=8#Button VALUE="connect to port|\$connect(\@address@,\@port@)"&gt;
-       &lt;PARAM NAME=10#Button VALUE="window|\$detach()"&gt;
-	</PRE>
-	<P>
-	  <A NAME="fields"></A>
-      <DT><B>Input fields</B>
-      <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Input VALUE=&quot;<B><I>fieldname</I></B>[#<I><B>length</B></I>]|<B><I>initial text</I></B>[|<B><I>action</I></B>]&quot;&gt;</TT>
-      <DD><B><I>number</I></B> is the sequence number and determines the place
-	of the field on the row.
-	<P>
-      <DD><A NAME="fieldreference"><B><I>fieldname</I></B></A> is a
-	symbolic name to reference the input field. A reference may be used in 
-	<A HREF="#buttonaction"><B><I>button actions</I></B></A> and
-	is constructed as follows:
-	<TT>\@<B><I>fieldname</I></B>@</TT>
-	The <B>\@fieldname@</B> macro will be replaced by the string entered in
-	the text field.
-	<P>
-      <DD><B><I>length</I></B> is the length of the input field in numbers of
-	characters.
-	<P>
-      <DD><B><I>initial text</I></B> is the text to be placed into the input
-	field on startup
-	<P>
-      <DD><B><I>action</I></B> may be used similar to a 
-	<A HREF="#buttonaction"><B><I>button action</I></B></A>. This action 
-	will be used if the users presses Return in the inputfield. Leave
-	empty if you only want to use a button to send the text!
-	<P>
-      <DD><A NAME="Example_3"><B>Examples:</B></A><BR>
-	<FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
-	  examples for <A HREF="#Example_2">buttons</A> before.)</FONT>
-	<PRE>
-	  &lt;PARAM NAME=3#Input VALUE="help#10|"&gt;
-	  &lt;PARAM NAME=7#Input VALUE="address|www.first.gmd.de"&gt;
-	  &lt;PARAM NAME=9#Input VALUE="port#5|4711"&gt;
-	</PRE>
-	<P>
-    </DL>
-    <A NAME="Script"></A>
-    <H3>The Script Module</H3>
-    The script module gives a very simple implementation of an <I>input
-      triggered</I> script executor. This means it sends text to the remote host
-    when the received text matches a <I>pattern</I> that can be programmed. It
-    executes each <I>pair of pattern and text</I> only once and stops working
-    after all patterns have been matched. It will start working again upon a
-    new connection.
-    <P>
-      To load the module include the following tag into the <B>.html</B> file
-      (example):<P>
-      <TT><B>&lt;PARAM NAME=module#<I>1</I> VALUE="<I>Script</I>"&gt;</B>
-      </TT><P>
-      Below is a description of possible PARAM tags and a description of script:
-    <DL>
-      <DT><B>Scripts:</B>
-      <DD><TT>&lt;PARAM NAME=script VALUE=&quot;<B><I>pattern</I></B>|<B><I>text</I></B>|<B><I>...</I></B>&quot;&gt;</TT>
-      <DD>A script contains of pairs of <I>pattern</I> and <I>text</I> strings.
-	If the pattern is matched against the output from the remote host,
-	the corresponding text will be sent. Each pattern will match only
-	<B>once</B> per connected session.
-	Thus it is possible to program an autologin as follows:<BR>
-	<TT><B>"login:|leo|Password:|mypassword|leo@www|ls"</B></TT>
-	Newlines will be added automatically to the string sent! At the
-	moment the order of the pattern and text pairs is <I>not</I> relevant.
-	<P>
-        It is possible to prompt the user for input if a match occurs. If the
-        corresponding <I>text</I> is a string enclosed in braces ([] or {}) a
-        dialog window is opened with <I>text</I> as prompt. A special case 
-        is an empty prompt in which case the <I>pattern</I> will be shown as 
-        prompt. &quot;[Your name:]&quot; would open a dialog window with the
-        text &quot;Your name&quot; as prompt. Curly braces have a special
-        meaning; any user input will be shown as &quot;*&quot; which makes
-        it possible to program password prompts. Example: 
-        &quot;{Your password:}&quot;.<P>
-        A special match like: &quot;login:|[]&quot; can be used to open a
-        dialog and display &quot;login:&quot; as prompt. This works for
-        &quot;{}&quot; as well.
-    </DL>
-    <P>
-      <A NAME="MudConnector"></A>
-    <H3>MudConnector</H3>
-    This module is a special edition for the <A
-      HREF="http://www.mudconnect.com/">Mud Connector</A>. It features a list of
-    MUDs and a few buttons to connect, disconnect and get infos about the MUDs.
-    A very nice example for a specialized module.
-    To load the module include the following tag into the <B>.html</B> file
-    (example):<P>
-      <TT><B>&lt;PARAM NAME=module#<I>1</I> VALUE="<I>MudConnector</I>"&gt;</B>
-      </TT>
-      The MudConnector expects the following PARAM tags:
-    <DL>
-      <DT><B>Mudlist URL:</B>
-      <DD><TT>&lt;PARAM NAME=mudlist VALUE=&quot;<B><I>URL</I></B>&quot;&gt;</TT>
-      <DD>The URL should be a file containing line by line the <I>MUD name</I>, the
-	<I>Mud address</I> and the <I>MUD port</I>, separated by tabulators.
-	The first line in the file should be the number of MUDs in the file.
-	<P>
-      <DD><B>Example:</B><BR>
-	<TT>&lt;PARAM NAME=mudlist VALUE="http://www.mud.de/~leo/mudlist.data"&gt;</TT>
-	<P>
-    </DL>
-    <P>
-    <HR>
-    <A NAME="SetupAppWrapper"></A>
-    <H2>The Applet Wrapper Setup</H2>
-    The applet wrapper is an applet that does nothing else than loading the, for
-    example, telnet applet. To understand why this is necessary you have to look
-    at the experiences we have made.<P>
-      Simply using the telnet applet in the following manner:<P>
-      <TT>&lt;APPLET CODE="telnet.class" WIDTH=600 HEIGHT=480&gt;</TT>
-      and using the <A HREF="#detach">detach</A> function stops the applet after
-      you have detached the applet and want to browse the web again. It seems that
-      the Web browser stops all threads connected to the applet if you leave the
-      page where the applet is located and thus it doesn't even update its display
-      anymore.<P>
-      We have found out that this is not true for applets loaded within the applet
-      on the page (e.g. the appWrapper). Look at the following description on how
-      to setup the appWrapper. It will probably work with any given applet out on
-      the web!
-    <P>
-      <PRE>
-	&lt;APPLET CODE="appWrapper.class" WIDTH=600 HEIGHT=480&gt;
-	&lt;PARAM NAME=applet VALUE="<I>telnet</I>&gt;
-
-	&lt;!-- optional (WIDTH and HEIGHT should be changed!) --&gt;
-	&lt;PARAM NAME=startButton VALUE="<I>text</I>"&gt;
-	&lt;PARAM NAME=stopButton VALUE="<I>text</I>"&gt;
-	&lt;PARAM NAME=frameTitle VALUE="<I>text</I>"&gt;
-	&lt;!-- all other telnet applet parameters go here --&gt;
-	&lt;/APPLET&gt;
-      </PRE>
-      The <B>appWrapper</B> knows only about the PARAM tag <B>applet</B>, which is
-      the applet to be loaded. In this case it must be in the same directory as the
-      <B>appWrapper.class</B>. Refer to the <A HREF="#Example_1">telnet example</A>
-      above for the telnet parameters.<P>
-      If a <B>startButton</B> is specified the applet won't start automatically,
-      but instead the appWrapper will display a button with the <I>text</I> on
-      it. The <B>stopButton</B> defines the text that appears on the button when
-      the applet is loaded and running and <B>frameTitle</B> specifies the frame
-      title text of the window the applet runs in.
-    <P>
-    <HR>
-    <A NAME="SetupProxy"></A>
-    <H2>Setting up the proxy server</H2>
-    There are two proxy servers provided with the telnet applet. The first one
-    is written in java and does support connections to <B>one</B> target host only
-    and the second one is written in C and supports different targets (called
-    relay daemon).<P>
-    <H4 ALIGN=CENTER>
-      [<A HREF="#javaproxy">Java Proxy</A> |
-      <A HREF="#relayd">Simple Relay Daemon</A> |
-      <A HREF="#mrelayd">Relay Daemon</A>]
-    </H4>
-    <P>
-      <A NAME="javaproxy"></A>
-    <H3>The Java Proxy Server</H3>
-    The proxy server is a small java program
-    to overcome the security restrictions of java capable web browsers.<P>
-      The proxy is used to redirect a connection to a given
-      host. Usually an applet can only connect to the web server it has
-      been loaded from. Installing the proxy on your web server allows the
-      applet to connect to a host you would like to connect to.<P>
-    <DL>
-      <DT><B>How to run the proxy application?</B>
-      <DD>To run the proxy you require the following:
-	<OL>
-	  <LI>A java interpreter (usually included in the JDK)
-	  <LI>A compiled version of the proxy 
-	    (<A HREF="tools/proxy.class">proxy.class</A>)
-	</OL><P>
-	  On the <I>WWW-Server command line</I> run the proxy server as follows:
-	  <TT>java proxy 9999 remotehostname 23
-	  </TT>
-	  This lets the proxy listen on port 9999 and it redirects
-	  all connections to the host "remotehostname" at port 23. You
-	  can leave the port parameter out if it is 23 (telnet port).
-	<P>
-	  The proxy should start with something like the output below:
-	  <TT>
-	    proxy: destination host is remotehostname at port 23
-	    proxy: listening on port 9999
-	  </TT>
-	  Upon successful connection the output should produce something
-	  like this:
-	  <TT>
-	    proxy: accepted connection from augra.first.gmd.de
-	    proxy: connecting www.first.gmd.de &lt;-&gt; remotehostname
-	  </TT>
-      <DT><B>How to shut down the proxy?</B>
-      <DD>To shut down the proxy press ^C (Ctrl+C or Strg+C on a german
-	keyboard) if you have startet it normally. More advanced users
-	will run the proxy like 
-	<TT>java proxy 9999 remotehost 23 >&amp; errorlog &amp;</TT>
-	to put it into the background. The "errorlog" file should then
-	contain any messages. You can kill that process by looking for
-	the process id (ps | grep proxy) and issuing the kill 
-	&lt;processid&gt; command (this applies to UNIX only).
-	<P>
-      <DT><B>I get an error message like "class proxy not found"!</B>
-      <DD>You may have to set the CLASSPATH environment variable to
-        point to the current directory or to the directory where
-	proxy.class is located.
-    </DL>
-    <P>
-      <A NAME="relayd"></A>
-    <H3>The Simple Relay Daemon</H3>
-    The <I>simple</I> relay daemon works just like the
-    <A HREF="#javaproxy">proxy</A> above, but is a C version. It allows
-    connections only to a specified host and port which is preferrable for
-    security reasons. You can run the program (after compiling it) with the
-    following command line:
-    <TT>
-      relayd serverport targethost targetport
-      or just
-      relayd serverport targethost
-    </TT>
-    It will then listen on the <I>serverport</I> of the machine you started it and
-    connect to the <I>targethost</I>. The standard <I>targetport</I> is 23.
-    <P>
-      <A NAME="mrelayd"></A>
-    <H3>The Relay Daemon</H3>
-    The relay daemon is a program written by Marcus Mei&szlig;ner to support
-    different target hosts. It relays the connections from the applet to a
-    host that must be given to the relay daemon after connecting.<P>
-      The daemon expects a string
-      <TT>relay <I>address port</I></TT>
-      It must be run on the web server of the applet.
-      The relay daemon is not included in compiled form, because we would have to
-      support a number of platforms. However, you can write to us if you need a
-      special compiled version for your hardware platform.<P>
-      You should include the following tags to tell <A
-	HREF="#SetupTelnet">telnet</A> that it is supposed to use the prox server
-    <PRE>
-    &lt;PARAM NAME=proxy      VALUE="www.first.gmd.de"&gt;
-    &lt;PARAM NAME=proxyport  VALUE="31415"&gt;
-    </PRE>
-    <HR>
-    <A NAME="SourceDoc"></A>
-    <H1>Source Code Documentation</H1>
-    The Source Code of <B>The Java<SUP>(tm)</SUP> Telnet Applet</B> is available
-    under the terms of the <A HREF="http://www.fsf.org/copyleft/gpl.html"><B>GNU
-	General Public License</B></A> as documented in the file <A
-      HREF="../COPYING">COPYING</A>. In case you would like to use the packages as
-    libraries please apply the <A HREF="http://www.fsf.org/copyleft/lgpl.html"><B>
-	GNU Library General Public License</B></A> as documented in the file <A
-      HREF="../COPYING.LIB">COPYING.LIB</A>.<P>
-      Select from the structure below, what you would like to see. Each file
-      contains a <B>Version:</B> field determining its current status and version.
-      If you are not sure to have the most current version, please
-      <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet/index.download.html">
-	look here</A>.<P>
-      If you are unsure, whether you've got the newest version, compare your
-      copy of the file <A HREF="../REVISION"><B>REVISION</B></A> and this
-      <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet/REVISION">
-	<B>REVISION</B></A>, which is a direct link to the
-      <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet/">home page</A>.
-      The latest changes are documented in the file
-      <A HREF="../CHANGES"><B>CHANGES</B></A>.
-    <UL>
-      <LI><A HREF="Source/appWrapper.html">appWrapper.java</A>
-      <LI><A HREF="Source/telnet.html">telnet.java</A>
-	<UL>
-	  <LI><A HREF="Source/Package-socket.html">Socket Package</A>
-	    <UL>	  
-	      <LI><A HREF="Source/socket.TelnetIO.html">socket/TelnetIO.java</A>
-	      <LI><A HREF="Source/socket.StatusPeer.html">socket/StatusPeer.java</A>
-	    </UL>
-	  <LI><A NAME="SourceTerminal"></A>
-	    <A HREF="Source/Package-display.html">Display Package</A>
-	    <UL>
-	      <LI><A HREF="Source/display.CharDisplay.html">display/CharDisplay.java</A>
-	      <LI><A HREF="Source/display.SoftFont.html">display/SoftFont.java</A>
-	      <LI><A HREF="Source/display.Terminal.html">display/Terminal.java</A>
-	      <LI><A HREF="Source/display.TerminalHost.html">display/TerminalHost.java</A>
-	      <LI><A HREF="Source/display.vt320.html">display/vt320.java</A>
-	    </UL>
-	  <LI><A NAME="SourceModules"></A>
-	    <A HREF="Source/Package-modules.html">Module Package</A>
-	    <UL>
-	      <LI><A HREF="Source/modules.Module.html">modules/Module.java</A>
-	      <LI><A HREF="Source/modules.ButtonBar.html">modules/ButtonBar.java</A>
-	      <LI><A HREF="Source/modules.Script.html">modules/Script.java</A>
-	      <LI><A HREF="Source/modules.MudConnector.html">modules/MudConnector.java</A>
-	    </UL>
-	</UL>
-      <LI><A HREF="Source/IOtest.html">IOtest.java</A>
-      <LI><A HREF="Source/CharDisplayTest.html">CharDisplayTest.html</A>
-      <LI>Tools
-	<UL>
-	  <LI><A HREF="Source/proxy.html">tools/proxy</A>
-	  <LI><A HREF="Source/redirector.html">tools/redirector</A> (proxy.java)
-	  <LI><A HREF="../tools/relayd.c">tools/relayd.c</A> (simple proxy)
-	  <LI><A HREF="../tools/mrelayd.c">tools/mrelayd.c</A> (enhanced proxy)
-	</UL>
-    </UL>
-    <HR>
-    <A HREF="../index.html">
-      <IMG ALIGN=LEFT SRC="images/left.gif" BORDER=0 ALT="[BACK]"></A>
-    <B>Get the <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">latest
-	version</A> here!</B> <BR>
-    <!-- html-ts start -->
-    Last modified: Wed Jul 23 14:55:15 1997 by Matthias L. Jugel
-    <!-- html-ts end -->
-  </BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/user.html b/web/root/telnet/Documentation/user.html
deleted file mode 100644
index 404b750814..0000000000
--- a/web/root/telnet/Documentation/user.html
+++ /dev/null
@@ -1,288 +0,0 @@
-<!doctype HTML public "-//W30//DTD W3 HTML 3.0//EN">
-<HTML>
-  <HEAD><TITLE>The Java(tm) Telnet Applet: User Pages</TITLE></HEAD>
-  <BODY BGCOLOR="#ffffff">
-    <H1>The <A HREF="http://java.sun.com/">Java<SUP>(tm)</SUP></A>
-      Telnet Applet: User Pages</H1>
-    <P ALIGN=LEFT>
-      &copy; 1996-98 <A HREF="mailto:leo@first.gmd.de">Matthias L. 
-	Jugel</A>, <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">
-	Marcus Mei&szlig;ner</A> 
-    <HR>
-    <P>
-      To show you that this applet is actually <I>useful</I>, we have created 
-      this page of users. All those pages below have some sort of service, 
-      provided through <A HREF="whatis.telnet.html">telnet</A> and use the 
-      applet to make it easier to access their sites. This may either be some 
-      <I>text-based virtual reality</I> or a <I>chat line</I>. Even more 
-      promising is the usage for <I>library catalogues</I> or <I>online 
-	information systems</I>. While looking around on the net we found that 
-      a lot of users put the applet on their private page to access their 
-      local accounts via the web.<P>
-      <A HREF="http://www.snymor.edu/~drewwe/">Bill Drew</A>, Systems 
-      Librarian, SUNY College of Agriculture and Technology, wrote an
-      <A HREF="http://www.ariadne.ac.uk/issue8/java-telnet/intro.html">
-	<B>Article</B></A> about using the applet to access his libraries OPAC
-      system and a German Internet Newsletter 
-      <A HREF="http://www.intern.de/7_97_3.htm"><B>INTERNET Intern</B></A>
-      (german) found it useful.<P>
-      We are <I>happy to be referred</I> by the 
-      <A HREF="http://library.usask.ca/hytelnet"><B>HYTELNET</B></A> resource
-      collection of <A HREF="whatis.telnet.html">telnet</A>-accessible 
-      <I>library services</I> and <A HREF="http://www.mudconnect.com/">
-	<B>The Mud Connector</B></A>, the most comprehensive list of 
-      <I>text-based virtual realities</I> on the web. The Applet is also 
-      mentioned on the <A HREF="http://www.cs.utk.edu/~shuford/terminal/pc_emulation.html#internet"><B>Video Terminal Informations</B></A> page.
-      This is the right place to look for <I>native</I> terminal emulation 
-      programs.<P>
-      <B>The <A HREF="http://java.sun.com/">Java<SUP>(tm)</SUP></A> Telnet 
-	Applet</B> is <A HREF="http://www.gnu.ai.mit.edu/software/java/gnu-recommended-software.html#Applets">recommended GNU Software</A>.
-    <P>
-      All Resources, alphabetically sorted:
-    <P>
-    <DL>
-      <DT><B><I><BIG>S</BIG>ERVICES</I></B><HR>
-    <DD><DL>
-	<DT><B>Information Provider</B>
-	<DD><A HREF="http://library.usask.ca/hytelnet">HYTELNET</A>
-	  (referring page)
-	<DD><A HREF="http://www.mudconnect.com/">The Mud Connector</A>
-	  (<A HREF="http://www.mudconnect.com/java/Telnet/index.cgi">
-	    <I>telnet</I></A>)
-	  <P></DL>
-      <DD><DL>
-	  <DT><B>Libraries:</B>
-	  <DD><A HREF="http://opac.uni-erlangen.de/">University of Erlangen/OPAC</A>
-	    (<A HREF="http://opac.uni-erlangen.de/Telnet/telnet.html">
-	      <I>telnet (german)</I></A>)
-	  <DD><A HREF="http://jerome.cs.unm.edu/">ISTEC Libraries</A>
-	    (<A HREF="http://jerome.cs.unm.edu/Telnet/welcome.html">
-	      <I>telnet</I></A>)
-	  <DD><A HREF="http://www.ub.ku-eichstaett.de/">Katholische Universit&auml;tsbibliothek Eichst&auml;tt</A> - german
-	    (<A HREF="http://www.ub.ku-eichstaett.de/Telnet/jvtopac02.html"><I>telnet</I></A>)
-	  <DD><A HREF="http://www.lib.kth.se/">Royal Institute of Technology Library, Stockholm, Sweden</A>
-	    (<A HREF="http://www.lib.kth.se/telnet/telnete.html"><I>telnet</I></A>),
-	    (<A HREF="http://www.lib.kth.se/telnet/mattelnete.html"><I>telnet to Mathematics Library</I></A>)
-
-	  <DD><A HREF="http://www.falcon.edu/catalog/">Falcon Catalog</A>
-	    (<A HREF="http://www.falcon.edu/catalog/JavaTelnet/"><I>telnet</I></A>)
-	  <DD><A HREF="http://library.princeton.edu/">Princeton University
-	      Library</A>
-	    (<A HREF="http://library.princeton.edu/catalogs_and_databases/online_catalog.html"><I>telnet</I></A>)
-	  <DD><A HREF="http://www.buc.unican.es/">BIBLIOTECA de Universidad de Cantabria</A> (spanish)
-	    (<A HREF="http://www.buc.unican.es/capel2.htm"><I>telnet</I></A>)
-	  <DD><A HREF="http://snymoraa.cs.snymor.edu/pages/library/">SUNY
-	      Morrisville College Library</A>
-	    (<A
-	      HREF="http://snymoraa.snymor.edu/pages/library/telnet/netopac.htmlx">
-	      <I>telnet</I></A>)
-	    <P></DL>
-	<DD><DL>
-	    <DT><B>Remote System Access:</B>
-	    <DD><A HREF="http://www.crpa.it/agrishar/bbs">AgriShare BBS</A>
-	      (<A HREF="http://www.crpa.it/agrishar/bbs/Telnet/">
-	      <I>telnet</I></A>)
-	    <DD><A HREF="http://cereal.mv.com/">Cereal BBS</A>
-	      (<A HREF="http://cereal.mv.com/javatelnet/telnet/tel.html">
-		<I>telnet</I></A>)
-	    <DD><A HREF="http://www.cu-muc.de/">Connection Universe</A>
-	      (<A HREF="http://www.cu-muc.de/chat/"><I>telnet</I></A>)
-
-	    <DD><A HREF="http://www.deltronix.com/">Deltronix Online</A>
-	      (<A HREF="http://www.deltronix.com/public/telnet_deltronix.html"><I>telnet</I></A>) - Online Games, Message areas, Web hosting, Since 1988
-	    <DD><A HREF="http://dtk.campus.luth.se/">DTK homepage</A> - swedish
-	      (<A HREF="http://dtk.campus.luth.se/telnet/"><I>telnet</I></A>)
-	    <DD><A HREF="http://www.edenbbs.com">Eden BBS</A>
-	      (<A HREF="http://www.webprecision.com/telnet/edengate.htm">
-		<I>telnet</I></A>)  
-	    <DD><A HREF="http://bbs.exo.com/">Exo BBS</A>
-	      (<A HREF="http://bbs.exo.com/telnet.html"><I>telnet</I></A>)
-	    <DD><A HREF="http://shell.enteract.com/">EnterAct</A>
-	      (<A HREF="http://shell.enteract.com/~horvath/Telnet/"><I>telnet</I></A>)
-	    <DD><A HREF="http://infectionflux.myriad.net/">infection flux</A>
-	      (<A HREF="http://infectionflux.myriad.net/~tony/Telnet/index.html"><I>
-		  telnet</I></A>)
-	    <DD><A HREF="http://www.inconnect.com/">INTERNET CONNECT</A>
-	      (<A HREF="http://www.inconnect.com/telnet.html"><I>telnet</I></A>)
-	    <DD><A HREF="http://206.251.72.99/">ITEX Online</A>
-	      (<A HREF="http://206.251.72.99/java/Telnet/telnet_itex.net.html"><I>
-		  telnet</I></A>)
-	    <DD><A HREF="http://jungle.olivet.edu/">Jungle BBS</A>
-	      (<A HREF="http://jungle.olivet.edu/telnet/"><I>telnet</I></A>)
-	    <DD><A HREF="http://home.kiss.de/">KISS</A> - Kaiserslautern Internet
-	      Solutions Service GmbH
-	      (<A HREF="http://home.kiss.de/Telnet/login.html"><I>telnet</I></A>)
-	    <DD><A HREF="http://www.nyx.net/">Nyx.net</A> - A public access UNIX
-	      System
-	      (<A HREF="http://nyx.nyx.net:8002/"><I>telnet</I></A>)
-	    <DD><A HREF="http://pointblank.com/">Point Blank BBS</A>
-	      (<A HREF="http://pointblank.com/"><I>telnet</I></A>)
-	    <DD><A HREF="http://remcen.ehhs.cmich.edu/">REMCentral</A>
-	      (<A HREF="http://remcen.ehhs.cmich.edu/telnet.html"><I>telnet</I></A>)
-	    <DD><A HREF="http://www.southex.net/">SouthEX Interactive</A>
-	      (<A HREF="http://www.southex.net/logon.htm"><I>telnet</I></A>)
-	    <DD><A HREF="http://www.thegate.net/">The Gate BBS</A>
-	      (<A HREF="http://www.thegate.net/"><I>telnet</I></A>)
-	    <DD><A HREF="http://TheMatrix.com/">The Matrix.com</A>
-	      (<A HREF="http://TheMatrix.com/telnet.html"><I>telnet</I></A>)
-	    <DD><A HREF="http://www.tiga.com.au/">TIGA - Total Interactive Gaming 
-		Australia</A> (<A HREF="http://www.tiga.com.au/telnet/jtelnet.htm">
-		<I>telnet</I></A>)
-	    <DD><A HREF="http://w3.uokhsc.edu/">The University of Oklahoma Health
-		Sciences Center</A>
-	      (<A HREF="http://www.uokhsc.edu/home/service/telnet/"><I>telnet</I></A>)
-	    <DD><A HREF="http://uncnsrd.mt-kisco.ny.us/">UNCENSORED! BBS</A>
-	      (<A HREF="http://uncnsrd.mt-kisco.ny.us/Telnet/"><I>telnet</I></A>)
-	    <DD><A HREF="http://undergnd.metrobbs.com/">Metropolis UNDERGROUND (<i>defect</i>)</A>
-	      (<A HREF="http://undergnd.metrobbs.com/"><I>telnet</I></A>)
-	      <P></DL>
-	  <DT><B><I><BIG>R</BIG>ECREATIONAL</I></B><HR>
-	  <DD><DL>
-	      <DT><B>Text-based Virtual Reality (MUD):</B>
-	      <DD><A HREF="http://abyss.nucleus.com/">The Abyss</A>
-		(<A HREF="http://abyss.nucleus.com/telnet.html"><I>telnet</I></A>)
-	      <DD><A HREF="http://www.arctic.org/">ArcticMud</A>
-		(<A HREF="http://www.arctic.org/help/java_telnet.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://www.niweb.com/pgregg/">Crossed Swords</A>
-		(<A HREF="http://www.niweb.com/pgregg/telnetxswords.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://fmmikek.ftech.net/cuckoo/">the cuckoo's nest</A>
-		(<A HREF="http://fmmikek.ftech.net/cuckoo/Telnet/"><I>telnet</I></A>)
-	      <DD><A HREF="http://hell.wh8.tu-dresden.de/~delusion/">Dimension *X*</A>
-		(<A HREF="http://hell.wh8.tu-dresden.de/~delusion/connect.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://mud.skool.com/">Dimension *X*</A>
-		(<A HREF="http://mud.skool.com/telnet/">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://www.erols.com/moodyg/">Dragon Bane</A>
-		(<A HREF="http://moodyg.erols.com/Telnet/drgnbn.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://dragon.fmi.uni-passau.de/">Dragon MUD</A>
-		(<A HREF="http://dragon.fmi.uni-passau.de/Telnet/telnet.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://www.empire1.com/emperor/empire.html">Empire MUD</A>
-		(<A HREF="http://www.empire1.com/emperor/Empire-MUD/EmpireMUD.html"> <I>telnet</I></A>)
-<!-- ist zwar da, connected aber nicht
-	      <DD><A HREF="http://mud.dwango.com/~mud/">eQuoria</A>
-		(<A HREF="http://mud.dwango.com/~mud/side.html"> <I>telnet</I></A>)
--->
-	      <DD><A HREF="http://ff.mud.de">FinalFrontier</A> - german  
-		(<A HREF="http://ff.mud.de/play.html"> <I>telnet</I></A>)
-	      <DD><A HREF="http://www.insomnia-mud.com/~insomnia/">Insomnia-MUD</A>
-		(<A HREF="http://www.insomnia-mud.com/~insomnia/Telnet/mud.html"> <I>telnet</I></A>)
-	      <DD><A HREF="http://bw2.baub.bwk.tue.nl/">JoranMud/Fatal Dimensions</A>
-		(<A HREF="http://bw2.baub.bwk.tue.nl/Telnet/joranmud.html"> <I>telnet</I></A>)
-	      <DD><A HREF="http://www.ishar.com/">Ishar</A> 
-		(<A HREF="http://www.ishar.com/telnet/"><I>telnet</I></A>)
-<!-- scheint nicht mehr zu gehen
-	      <DD><A HREF="http://www.lr2.com/">Lost Rivers 2</A>
-		(<A HREF="http://www.graphweb.com/lr/java/connect.htm">
-		  <I>telnet</I></A>)
- -->
-<!-- dito
-	      <DD><A HREF="http://sdphul.ucsd.edu/antan/">The Marches of Antan</A>
-		(<A HREF="http://sdphul.ucsd.edu/antan/telnet/"><I>telnet</I></A>)
- -->
-<!-- dito
-	      <DD><A HREF="http://medievia.netaxs.com:8080/">Medievia</A>
-		(<A HREF="http://medievia.netaxs.com:8080/java/Telnet/javaconnect.html">
-		  <I>telnet</I></A>)
- -->
-<!-- dito
-	      <DD><A HREF="http://terminator.pathcom.com/~miked/ms/">The Midnight
-		  Sanctuary</A>
-		(<A HREF="http://mud.imperium.net/~mudmstr/Telnet/play_sanctuary.html">
-		  <I>telnet</I></A>)
- -->
-	      <DD><A HREF="http://mg.mud.de/">MorgenGrauen</A> - german
-		(<A HREF="http://mg.mud.de/online/"> <I>telnet</I></A>)
-	      <DD><A HREF="http://www.mudweiser.mudservices.com/">MUDweiser</A>
-		(<A HREF="http://www.mudweiser.mudservices.com/Telnet/"> <I>telnet</I></A>)
-	      <DD><A HREF="http://mud.lysator.liu.se/Telnet/">NannyMUD</A>
-		(<A HREF="http://mud.lysator.liu.se/java/Telnet/"> <I>telnet</I></A>)
-	      <DD><A HREF="http://www.reddragon.org/">Red Dragon LPmud</A>
-		(<A HREF="http://www.reddragon.org/Telnet/app.html"><I>telnet</I></A>)
-	      <DD><A HREF="http://www.df.lth.se/~tommy/robotech/index.html">Robotech MUSH</A>
-		(<A HREF="http://robotech.df.lth.se/~robotech/Telnet/index.test.html"><I>telnet</I></A>)
-	      <DD><A HREF="http://www.tsr.org/">An Age Of Legends: The Shadow Realms</A>
-		(<A HREF="http://www.tsr.org/Telnet/play.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://www.suvangi.com/">MUD Suvangi</A>
-		(<A HREF="http://www.suvangi.com/java/Telnet/telnet.html">
-		  <I>telnet</I></A>)
-	      <DD><A HREF="http://supermud3.com/">SuperMUD 3</A>
-	       (<A HREF="http://www.supermud3.com/game/java/index.html"><I>telnet</I></A>)
-	      
-	      <DD><A HREF="http://tapp.mud.de:8080/">TAPPMud</A>
-		(<A HREF="http://tapp.mud.de:8080/mud.html"><I>telnet</I></A>)
-	      <DD><A HREF="http://tragickingdom.ml.org">Tragic Kingdom</A>
-		(<A HREF="http://tragickingdom.ml.org/chat/"><I>telnet</I></A>)
-	      <DD><A HREF="http://terrafirma.terra.mud.org/">TerraFirmA</A>
-		(<A HREF="http://terrafirma.terra.mud.org/java/"><I>telnet</I></A>)
-	      <DD><A HREF="http://tecfa.unige.ch/moo/">TECFEA Moo</A>
-		(<A HREF="http://tecfa.unige.ch/moo/clients/jmud/telnet-tecfa.html">
-		  <I>telnet</I></A>) <B>it definitely needs update!</B>
-	      <DD><A HREF="http://mudhole.ehche.ac.uk/~tnt/">TNT</A>
-		(<A HREF="http://mudhole.ehche.ac.uk/~tnt/java.html"><I>telnet</I></A>)
-	      <DD><A HREF="http://UNItopia.uni-stuttgart.de/">UNItopia</A> - german
-		(<A HREF="http://UNItopia.uni-stuttgart.de/telnet/"><I>telnet</I></A>)
-	      <DD><A HREF="http://wilma.rz.uni-leipzig.de/mud/">Wunderland</A> - german
-		(<A HREF="http://wilma.rz.uni-leipzig.de/mud/telnet.html">
-		  <I>telnet</I></A>)
-	    </DL><P>
-	      <DD><DL>
-		  <DT><B>Chats & Talkers:</B>
-		  <DD><A HREF="http://apollos.ttu.ee/">Banalim</A>
-		    (<A HREF="http://apollos.ttu.ee/valitsus/Telnet/"><I>telnet</I></A>)
-		  <DD><A HREF="http://cafe.guam.net/">Cafe Guam</A>
-		    (<A HREF="http://http://cafe.guam.net/cafe2000/"><I>Cafe 2000</I></A>)
-<!-- down
-		  <DD><A HREF="http://crime.stack.nl/~rjkap/java.html">Capsi Chat</A>
-		    (<A HREF="http://crime.stack.nl/~rjkap/java.html"><I>telnet</I></A>)
- -->
-		  <DD><A HREF="http://www.talker.com/xena/">Xena: Chakrams & Scrolls</A>
-		    (<A HREF="http://www.talker.com/xena/java/"><I>telnet</I></A>)
-		  <DD><A HREF="http://www.talkers.org/cn/">Cirrus Nebula</A>
-		    (<A HREF="http://www.talkers.org/cn/telnet.html"><I>telnet</I></A>)
-<!--
-		  <DD><A HREF="http://funcity.newaygo.mi.us/~hsh/">Home Sweet Home</A>
-		    (<A HREF="http://funcity.newaygo.mi.us/~hsh/chat/index.html">
-		      <I>telnet</I></A>)
- -->
-<!--
-		  <DD><A HREF="http://www.cs.ut.ee/~helger/muumi/">Moominvalley talker</A>
-		    (<A HREF="http://www.cs.ut.ee/~helger/muumi/connect/">
-		      <I>telnet</I></A>)
- -->
-		  <DD><A HREF="http://amaterasu.math.orst.edu:8080/~sharpej/information/backyard.html">The Backyard</A>
-		    (<A HREF="http://amaterasu.math.orst.edu:8080/~sharpej/information/telnet.html"><I>telnet</I></A>)
-<!--
-		  <DD><A HREF="http://www.zait.uni-bremen.de/Esperanto/VEK/">VEKejo
-		      (Esperanto)</A>
-		    (<A HREF="http://www.zait.uni-bremen.de/Esperanto/VEK/telnet.html">
-		      <I>telnet</I></A>)
- -->
-		  <DD><A HREF="http://uberworld.ml.org/">UberWorld</A>
-		    (<A HREF="http://uberworld.ml.org/telnet.html">
-		      <I>telnet</I></A>)
-		  <DD><A HREF="http://kunpux.cogsci.kun.nl/~splotch/">Splotch (some non Turing test)</A>
-		    (<A HREF="http://kunpux.cogsci.kun.nl/~splotch/start-java-telnet.html">
-		      <I>telnet</I></A>)
-		  <DD><A HREF="http://talkers.skl.com/~bourbons/">Bourbon Street</A>
-		    (<A HREF="http://talkers.skl.com/~bourbons/class/java.html">
-		      <I>telnet</I></A>)
-		    <P></DL>
-	      </DL>
-		<P>
-		<HR>
-		<A HREF="../index.html">
-		  <IMG ALIGN=LEFT SRC="images/left.gif" BORDER=0 ALT="[BACK]"></A>
-		<B>Get the 
-		  <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">latest
-		    version</A> here!</B> <BR>
-		<!-- html-ts start -->
-		Last modified: Wed Jul 23 15:00:43 1997 by Matthias L. Jugel
-		<!-- html-ts end -->
-  </BODY>
-</HTML>
diff --git a/web/root/telnet/Documentation/whatis.telnet.html b/web/root/telnet/Documentation/whatis.telnet.html
deleted file mode 100644
index ddc38c8219..0000000000
--- a/web/root/telnet/Documentation/whatis.telnet.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!doctype HTML public "-//W30//DTD W3 HTML 3.0//EN">
-<HTML>
-<HEAD><TITLE>The Java(tm) Telnet Applet: What is TELNET?</TITLE></HEAD>
-<BODY BGCOLOR="#ffffff">
-<H1>The <A HREF="http://java.sun.com/">Java<SUP>(tm)</SUP></A>
-    Telnet Applet: What is TELNET?</H1>
-<P ALIGN=LEFT>
-&copy; 1996-98 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>,
-<A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus Mei&szlig;ner</A> 
-<HR>
-<P>
-Telnet allows you to login to another <I>Internet location</I> to gain
-access to <B>databases</B>, <B>library catalogs</B>, and <B>interactive
-environments</B>. It works like a terminal connected to your computer,
-only that that computer may be far away.<P>
-If you want to connect directly to a remote computer you need to know a
-<I>valid login ID</I> usually given to you by the provider of the service. 
-<P>
-To get a list of telnet service providers, please have a look at our list of
-<A HREF="user.html"><B>Applet Users</B></A>.
-<P>
-<HR>
-<A HREF="../index.html">
-<IMG ALIGN=LEFT SRC="images/left.gif" BORDER=0 ALT="[BACK]"></A>
-<B>Get the <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">latest
-version</A> here!</B> <BR>
-<!-- html-ts start -->
-Last modified: Thu Mar 13 12:29:58 1997 by Matthias L. Jugel
-<!-- html-ts end -->
-</BODY>
-</HTML>
diff --git a/web/root/telnet/INSTALL b/web/root/telnet/INSTALL
deleted file mode 100644
index e34907fcb3..0000000000
--- a/web/root/telnet/INSTALL
+++ /dev/null
@@ -1,55 +0,0 @@
-** The Java(tm) Telnet Applet **
-
--------------------------------------------------------------
-If you want to use the classes only do the following:
-
-* UNIX:
-	gzip -cd telnet.tgz | tar xf - \*.class
-
-* DOS/Windows:
-	pkunzip -d telnet.zip
-	* Maybe you can unzip it by just clicking on the
-	  package or Netscape knows how to handle .zip.
-
-This will extract the classes and create the required 
-directory structure (/ is \ on DOS/Windows):
-
-	telnet.class
-	socket/TelnetIO.class
-	socket/StatusPeer.class
-	display/CharDisplay.class
-	display/Terminal.class
-	display/TerminalHost.class
-	display/vt320.class
-	modules/ButtonBar.class
-	modules/Script.class
-
--------------------------------------------------------------
-In case you like to compile the sources yourself extract
-everything and type the following:
-
-	make realclean
-	make telnet.class
-
-The Makefile will compile all classes recursively even though
-the java compiler would do this by itself when running 
-"java telnet.java".
-
-Just typing "make" will print you all available make targets:
-
-	make test         - compile applet and run appletviewer
-	make telnet       - compile standalone and run telnet
-	make doc          - create documentation
-	make clean        - delete backup files
-	make realclean    - make clean and delete .class files
-
--------------------------------------------------------------
-
-Hint: Always remember that the applet can only connect to the
-      server it has been loaded from! You will get a security
-      exception when trying to connect to some other host.
-
-      If you load your applet locally via the file:// URL
-      don't forget to set the CLASSPATH environment variable
-      to the directory, where the telnet.class resides _before_
-      you start your appletviewer or web browser!
diff --git a/web/root/telnet/IOtest.class b/web/root/telnet/IOtest.class
deleted file mode 100644
index 7e90843c7e059e3751d0d85810b5533e4a26992b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1589
zcmX^0Z`VEsW(Hk`Yz_tk4hAa@25Sxm8x96f4u)tBh8PZpOm+qbc7_mkhER5fFm{G;
zc7_OchDdgX6fOoH21PCg0T98<pafzmg9sHa23`hL5TOPl)HxaW7&N#T_!%_07`Pd<
zKuWZ^7&sYpKrCHO22KV&5Lcgv!H&TQL|E`J_%oRCFa&_OK@28548aVhJPc6`hCB>$
z46Zy3@gRu=kdh<@V;+WN5G$F%oQENu!Gnh(i@}VMfq{{MSwqtkOolNsuxMyTJ25hF
zXn1-?J9%n`F*1m1_+%xPCF<uS=B4WgmlS2@rCWp4V-*i$WMI{Zb^_^R(|{TvrV)+B
zgfK=1A)845&>#gLe|Jwm1wTh$S36!t1_6j*fMalQxPOofNJtnW6zt*Z<D(D|<nJ30
zV#mn9VUwJbnU`5&$H>5D17)x{=clAHGO&62m!uY#FfwpM7<wQNFfwrXK)BY74E#Qs
zd8vM-xk;%-A&E&jsf-Mw5I1J#>wEgURwSnulw{`TStE-F6lLa>1eX-0Cgxf*G6+G`
zBZmq|Fh4mlCoHij6J#jVWI><e{N(J^68(_WoV?T$Pk(Dh2F`%Q;^OlBq7*Ae2Cm@z
z(xT*4w@i>Tm_VT~j3vMr8CVjF(u)}xn3GD=7#Y};^YijjlS>#GxKc8U;T*<PMh1@b
z)Dox4lGI{G2KLOnl++6UG)4wtP!Q;&`VL7H$#0Ad0${aZpZO<cfh|E2f+PVHRl$|T
zC8@bE<G>n2GK%ucL4m-?z?PGmmtK;=$iSHc3Z0C6P|UF9<fmumSurxO<R)h3F)}dc
zmzFRx@E1VBI59apq$n{tm63rRERvJQ$iP-ml%HEr!pOj0l$xBHS(eJkz*3x=m%_-v
zj~2d+49vwPMT`tAB^jBZ04vGNXJlY5$t_@HkSobAP0mozFUc*?5ApB}jt_SXj(7Bn
zcMJ&daSh>RWMC;z%q(G0VBlonVF2X~4p3HMU|?Wn;9#(2Fk)b2uxDUk&}U#`00HjZ
z3<8nc8H6@7$Zup|U|?c!W?*380!vCTa50E7xPwJOO6`y|GcquOjMUoBAc)Y$1{Q>9
z;Q_N*8PXV>7#J8h8DbeMp*GKDU;-O&x`{zvh;18#h!Fb@1_dFe9SpKUtRNOMh{Xb8
zaqM7_-NYcjjX`ud1BZ|HHU_DUU}JR{^ucZfxzz~lO(O;d24e;e1``He22%zh1~Ue6
z26F}(1`7s721^Dt1}g>~1|tT225SZr1_Q94#2}7f@MZ8}U|=v{U||SkaA9C%-~yE~
zP^YFbu!2>|X>DVW*u&wY#SCI-GjC%M+r%KRy^TR`IRi^L$W1~Fk_<9%HwiJYFfcG^
zGO#mfF$gheGsrTiBiSj(kjmi3z`(%HAkN^&;0?Aji@^+PXDb5>12Y2ygSHT(&u#|Z
zNU;B9n71<s`z&YR@YUJIAhn4>QhOVNw6@kZ263kO|L=etz|0`TAPRSY6ayOr1A{CB
zGlM*XD1!on6oVpzB7+ix7K1d{Aqo&H7!tt_VP?=^$Y5||U}O-31_KiVBZC(>;vE6G
Cb~Skb

diff --git a/web/root/telnet/IOtest.java b/web/root/telnet/IOtest.java
deleted file mode 100644
index bbd0d26059..0000000000
--- a/web/root/telnet/IOtest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/* IOtest.java -- An example how to use the TelnetIO class
- * --
- * Author: Matthias L. Jugel
- *
- * Usage: compile with javac IOtest.java
- *        run program with java IOtest
- *
- * This is not an applet, but the idea might be used in one. 
- */
-
-import java.util.Vector;
-import java.io.*;
-import socket.*;
-
-/**
- * IOtest -- a test class for telnet i/o
- * --
- * @version	$Id: IOtest.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author	Matthias L. Jugel
- */
-class IOtest {
-
-  // create a new telnet io instance
-  static TelnetIO tio = new TelnetIO();
-
-  // skip any received data until the prompt appears
-  private static void wait(String prompt)
-  {
-    String tmp = "";
-    do {
-      try { tmp = new String(tio.receive(), 0); }
-      catch(IOException e) { e.printStackTrace(); }
-      System.out.println(tmp);
-    } while(tmp.indexOf(prompt) == -1);
-  }
-
-  // send a string to the remote host, since TelnetIO needs a byte buffer
-  // we have to convert the string first
-  private static void send(String str)
-  {
-    byte[] buf = new byte[str.length()];
-    str.getBytes(0, str.length(), buf, 0);
-    try { tio.send(buf); } catch(IOException e) {}
-  }
-
-  // this function is called when running the class with java IOtest
-  // looks very much like a modem login script ;-)
-  public static void main(String args[])
-  {
-    try {
-      tio.connect("localhost");
-      wait("login:");
-      send("<YOUR LOGIN NAME>\r");
-      wait("Password:");
-      send("<YOUR PASSWORD>\r");
-      wait("<YOUR SHELL PROMPT>");
-      send("touch /tmp/THIS_WAS_AN_APPLET\r");
-      tio.disconnect();
-    } catch(IOException e) { e.printStackTrace(); }
-  }
-}
diff --git a/web/root/telnet/Makefile b/web/root/telnet/Makefile
deleted file mode 100644
index e9a10316d3..0000000000
--- a/web/root/telnet/Makefile
+++ /dev/null
@@ -1,178 +0,0 @@
-#
-# This file is part of "The Java Telnet Applet".
-#
-# This 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, or (at your option)
-# any later version.
-#
-# "The Java Telnet Applet" 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 The JAVA Telnet Applet; see the file COPYING.  If not, write to 
-# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-#
-# $Id: Makefile,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
-
-OPT	= 
-DEBUG	= -g -deprecation
-JAVA	= java
-JAVAC	= javac
-JAVADOC = javadoc
-APPV	= appletviewer
-
-.SUFFIXES: .java .class .html
-
-.java.class:
-	$(JAVAC) $(OPT) $(DEBUG) $<
-
-.class.html:	
-	$(APPLV) $@
-
-HELP:
-	@echo 'To compile "The Java Telnet Applet" do one of the following:'
-	@echo '------------------------------------------------------------'
-	@echo 'make all          - compile all files to classes'
-	@echo 'make test         - compile applet and run appletviewer'
-	@echo 'make telnet       - compile standalone and run telnet'
-	@echo 'make chartest     - compile character display test and run'
-	@echo 'make proxy        - compile proxy server'
-	@echo 'make doc          - create documentation'
-	@echo 'make clean        - delete backup files'
-	@echo 'make realclean    - make clean and delete .class files'
-
-all: appWrapper.class telnet.class modules tools
-	@echo All classes created.
-
-test: appWrapper.class telnet.class
-	$(APPV) index.test.html
-
-telnet:	appWrapper.class telnet.class
-	$(JAVA) telnet localhost	
-
-chartest: CharDisplayTest.class
-	$(APPV) CharDisplay.html
-
-doc: 
-	$(JAVADOC) -d Documentation/Source -author -version \
-		display socket modules \
-	        telnet.java appWrapper.java \
-                tools/proxy.java CharDisplayTest.java IOtest.java
-
-tar:	
-	rm -f ../telnet.tgz ../telnet.zip
-	(cd ..; tar cf - Telnet | gzip - > telnet.tgz)
-	(cd ..; zip -r telnet.zip Telnet)
-
-bin-tar: all
-	rm -f ../classes.tgz ../classes.zip
-	(cd ..; tar cf - Telnet/*.class Telnet/*/*.class \
-                          Telnet/README Telnet/INSTALL Telnet/COPYING \
-		| gzip - > classes.tgz)
-	(zip -r ../classes.zip *.class */*.class README INSTALL COPYING)
-
-revision: 
-	grep @version *.java */*.java|awk '{split($$6,rev,".");printf("%-26.26s %2.2s.%-2.2s (%s)\n",$$1,rev[1],rev[2],$$7);}' > REVISION
-	rcs2log *.java */*.java > CHANGES
-	sed -e"s/package:.*$$/package: `date +%c`/" index.html > index.html.x
-	mv index.html.x index.html
-
-dist:	realclean all doc tar bin-tar
-	@echo DONE.
-
-clean:	
-	rm -f *~ */*~
-
-realclean:	clean
-	rm -f *.class */*.class
-	(cd tools; make clean)
-
-# dependencies
-
-appWrapper.class: \
-	appWrapper.java \
-	frame.java
-
-telnet.class: \
-	telnet.java \
-	display \
-	socket \
-	modules 
-
-# display classes and terminal emulation
-
-display: \
-	display/vt320.class
-
-display/vt320.class: \
-	display/vt320.java \
-	display/CharDisplay.java \
-	display/TerminalHost.java \
-	display/Terminal.java
-
-display/CharDisplay.java: \
-	display/SoftFont.java
-
-# socket io classes
-socket: \
-	socket/TelnetIO.class \
-	socket/TelnetWrapper.class
-
-socket/TelnetIO.class: \
-	socket/TelnetIO.java \
-	socket/StatusPeer.java
-
-# dynamical loaded modules
-modules: modules/ButtonBar.class \
-         modules/Script.class \
-         modules/TextLabel.class \
-         modules/MudConnector.class \
-	 modules/BSXModule.class
-
-modules/ButtonBar.class: \
-	modules/ButtonBar.java \
-	modules/Module.java \
-	frame.java
-
-modules/Script.class: \
-	modules/Script.java \
-	modules/Module.java
-
-modules/MudConnector.class: \
-	modules/MudConnector.java \
-	modules/Module.java
-
-modules/BSXModule.class: \
-	modules/bsx/BSXDisplay.java \
-	modules/bsx/BSXGraphic.java \
-	modules/bsx/BSXInputStream.java \
-	modules/bsx/BSXObject.java \
-	modules/bsx/BSXPolygon.java \
-	modules/bsx/BSXScene.java
-
-# tools
-tools: \
-	tools/proxy.class \
-	CharDisplayTest.class \
-	IOtest.class \
-	tools/mrelayd \
-	tools/relayd
-
-tools/proxy.class: \
-	tools/proxy.java
-
-CharDisplayTest.class: \
-	CharDisplayTest.java \
-	display/CharDisplay.java
-
-IOtest.class: \
-	IOtest.java \
-	socket/TelnetIO.java
-
-socket/TelnetWrapper.class: \
-	socket/TelnetWrapper.java \
-	socket/TelnetIO.java
diff --git a/web/root/telnet/README b/web/root/telnet/README
deleted file mode 100644
index 58f23de91d..0000000000
--- a/web/root/telnet/README
+++ /dev/null
@@ -1,55 +0,0 @@
-The Java Telnet Applet
-
-Authors: Matthias L. Jugel (leo@first.gmd.de)
-	 Marcus Mei�ner (msmeissn@cip.informatik.uni-erlangen.de)
-
-This applet is the result of our attempt to make a useful java application
-and to provide better access to the world of internet through the almost
-everywhere available Web browser.
-
-We hope you find this piece of software as useful as we do. The source code
-is publicly available under the GPL.
-
-If you encounter any problems, bugs or else don't hesitate to contact us.
-
-	http://www.first.gmd.de/persons/leo/java/Telnet
-
-will always have the newest version.
-
-This distibution should contain the following files and directories:
-
-README 			  - this file
-REVISION 		  - contains version information about .java files
-CHANGES                   - all changes are logged here
-INSTALL 		  - how to install the applet
-COPYING 		  - GNU General Public Licence
-COPYING.LIB 		  - GNU Library General Public Licence
-BUGS 			  - known bugs
-Makefile 		  - to create .class files and documentation
-
-telnet.java		  - the telnet applet/application
-socket/TelnetIO.java 	  - all telnet negotiation handling
-socket/StatusPeer.java 	  - a status peer interface
-display/Terminal.java 	  - an abstract terminal class
-display/TerminalHost.java - a virtual terminal host interface
-display/CharDisplay.java  - a general purpose character display
-display/vt320.java	  - a VT320/ANSI emulation class using CharDisplay
-modules/ButtonBar.java	  - a programmable button bar
-modules/Script.java	  - a simple script module
-
-The package contains also all precompiled classes!
-
-tools/IOtest.java 	  - an example program which works like a 
-			    script executor
-tools/TelnetWrapper.java  - another example program provided by
-			    George Ruban
-tools/CharDisplayTest.java- a test class for display/CharDisplay.java
-
-tools/proxy.java 	  - a simple proxy server
-
-*.html 			  - various test files
-examples/*.html           - example files for different usage
-
-Documentation/ 		  - documentation of the JAVA source code
-CVS/			  - revision control, contains the history of 
-			    the source code
diff --git a/web/root/telnet/REVISION b/web/root/telnet/REVISION
deleted file mode 100644
index 1be1ae1ddb..0000000000
--- a/web/root/telnet/REVISION
+++ /dev/null
@@ -1,20 +0,0 @@
-CharDisplayTest.java:       1.1  (1997/03/05)
-IOtest.java:                1.1  (1997/03/05)
-appWrapper.java:            1.9  (1997/07/24)
-telnet.java:                1.21 (1999/03/04)
-display/CharDisplay.java:   1.29 (1999/03/20)
-display/Terminal.java:      1.1  (1997/03/05)
-display/TerminalHost.java:  1.1  (1997/03/05)
-display/vt320.java:         1.60 (1999/03/20)
-modules/ButtonBar.java:     1.23 (1999/04/09)
-modules/Module.java:        1.3  (1997/03/24)
-modules/MudConnector.java:  1.3  (1999/04/09)
-modules/Script.java:        1.6  (1997/11/03)
-modules/TextLabel.java:     1.1  (1997/07/09)
-socket/StatusPeer.java:     1.1  (1997/03/05)
-socket/TelnetIO.java:       1.10 (1998/02/09)
-socket/TelnetIO.java:      Ge.   (Ruban)
-socket/TelnetWrapper.java:  -.   (added)
-socket/TelnetWrapper.java:  -.   (added)
-socket/TimedOutException.j */.   ()
-tools/proxy.java:           1.4  (1997/05/27)
diff --git a/web/root/telnet/TODO b/web/root/telnet/TODO
deleted file mode 100644
index 3c5da65f35..0000000000
--- a/web/root/telnet/TODO
+++ /dev/null
@@ -1,22 +0,0 @@
-The Java(tm) Telnet Applet 
-
--- TODO LIST --
-
-May, 27, 1997
-
-	* connection timeout
-
-tools/proxy.java
-	* additional log file
-
-March, 14, 1997
-
-
-display/vt320.java
-	* should add an edit line when using line mode
-	* vttest compliance
-	* 80/132 columns switching to resize the window instead of the font
-
-display/CharDisplay.java
-	* Needs to be refined. The Font resizing in one method and the usage
-	  of addNotify() is recommendet.
diff --git a/web/root/telnet/appWrapper.class b/web/root/telnet/appWrapper.class
deleted file mode 100644
index fac64e5ab34219192efc5e7af094d2e276e5eb2f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 5619
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
R*cm=CTn1M{x4;G5BLH6ks;&S4

diff --git a/web/root/telnet/appWrapper.java b/web/root/telnet/appWrapper.java
deleted file mode 100644
index 003da5ee47..0000000000
--- a/web/root/telnet/appWrapper.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/**
- * appWrapper -- applet/application wrapper
- * --
- * $Id: appWrapper.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Thu Jul 24 13:08:23 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-import java.applet.Applet;
-import java.applet.AppletStub;
-
-import java.awt.Frame;
-import java.awt.Event;
-import java.awt.Panel;
-import java.awt.Button;
-import java.awt.BorderLayout;
-import java.awt.Graphics;
-import java.awt.Color;
-import java.awt.FontMetrics;
-
-/**
- * The appWrapper is thought to make the applet itself independent from
- * the original context. This is necessary to be able to detach the applet
- * from the web browsers window without disconnecting it from events.
- * Note: This applet should work with any applet without changes.
- *
- * <DL>
- * <DT><B><PRE>&lt;PARAM NAME=&quot;applet&quot; VALUE=&quot;<I>applet</I>&quot;&gt;</PRE></B>
- * <DD>Defines the applet to be loaded by the appWrapper. State the applet 
- *     class name without &quot;.class&quot;!<P>
- * <DT><B><PRE>&lt;PARAM NAME=&quot;startButton&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
- * <DD>If this parameter is set the applet is not loaded until the user presses
- *     the button. This decreases first time download delay. The <I>text</I>
- *     given as value to the parameter is shown on the button. While loading
- *     the applet the message "Loading ..." is shown on the button.<P>
- * <DT><B><PRE>&lt;PARAM NAME=&quot;stopButton&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
- * <DD>This parameter defines the button text when the applet is loaded. When 
- *     pressing the button while the applet is running this causes the applet
- *     window to be destroyed and the applet is stopped.<P>
- * <DT><B><PRE>&lt;PARAM NAME=&quot;frameTitle&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
- * <DD>The <I>frameTitle</I> is the text that is shown in the title bar of the
- *     applet window.<P>
- * </DL>
- * @version $Id: appWrapper.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel
- */
-public class appWrapper extends Applet implements AppletStub, Runnable
-{
-  Thread loader = null;
-  
-  String appletName = null;
-  Applet applet = null;
-
-  Button startButton = null;
-  String startLabel, stopLabel, frameTitle;
-
-  frame f;
-  
-  /**
-   * Applet initialization. We load the class giving in parameter "applet"
-   * and set the stub corresponding to ours. Thus we are able to give
-   * it access to the parameters and any applet specific context.
-   */
-  public void init() {
-
-    // get the applet parameter
-    if((appletName = getParameter("applet")) == null) {
-      showStatus("appWrapper: missing applet parameter, nothing loaded");
-      System.err.println("appWrapper: missing applet parameter");
-      return;
-    }
-
-    setLayout(new BorderLayout());
-
-    // get the button and title parameters
-    if((startLabel = getParameter("startButton")) == null)
-      run();
-    else {
-      startButton = new Button(getParameter("startButton"));
-      add("Center", startButton);
-      if((stopLabel = getParameter("stopButton")) == null)
-        stopLabel = "STOP!";
-      if((frameTitle = getParameter("frameTitle")) == null)
-        frameTitle = "The Java Telnet Applet";
-    }
-    
-  }
-  
-  /**
-   * Load the applet finally. When using a button this creates a new frame
-   * to put the applet in.
-   */
-  public void run() {
-    if(applet == null) try {
-      applet = (Applet)Class.forName(getParameter("applet")).newInstance();
-      applet.setStub(this);
-    } catch(Exception e) {
-      System.err.println("appWrapper: could not load "+appletName);
-      e.printStackTrace();
-      return;
-    } else {
-      System.err.println("appWrapper: applet already loaded");
-      return;
-    }
-
-    if(startButton == null) {
-      add("Center", applet);
-      applet.init();
-    } else {
-      f = new frame(frameTitle);
-      f.setLayout(new BorderLayout());
-      f.add("Center", applet);
-      applet.init();
-      f.resize(applet.minimumSize());
-      f.pack();
-      f.show();
-    }
-    applet.start();
-
-    if(startButton != null)
-      startButton.setLabel(stopLabel);
-    
-    // stop loader thread
-    while(loader != null) {
-      if(f == null || !f.isVisible()) {
-        startButton.setLabel(startLabel);
-        loader.stop();
-        loader = null;
-      }
-      try { loader.sleep(5000); }
-      catch(InterruptedException e) {
-        e.printStackTrace();
-      }
-    }
-  }
-
-  /**
-   * This method is called when the applet want's to be resized.
-   * @param width the width of the applet
-   * @param height the height of the applet
-   */
-  public void appletResize(int width, int height) {
-    System.err.println("appWrapper: appletResize()");
-    if(applet != null) applet.resize(width, height);
-  }
-
-  /**
-   * Give information about the applet.
-   */
-  public String getAppletInfo()
-  {
-    String info = "appWrapper: $Id: appWrapper.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $\n";
-    if(applet != null)
-      info += applet.getAppletInfo();
-    return info;
-  }
-  
-  /**
-   * Give information about the appWrapper and the applet loaded.
-   */
-  public String[][] getParameterInfo()
-  {
-    String info[][];
-    String wrapper[][] = {
-      {"applet",   "String",   "appWrapper: Applet to load"},
-    };
-    if(applet != null) {
-      String tmp[][] = applet.getParameterInfo();
-      info = new String[tmp.length + 1][3];
-      System.arraycopy(tmp, 0, info, 1, tmp.length);
-    }
-    else info = new String[1][3];
-    System.arraycopy(wrapper, 0, info, 0, 1);
-      
-    return info;
-  }
-
-  /**
-   * Write a message to the applet area.
-   */
-  public void paint(Graphics g) 
-  {
-    String message;
-    if(applet != null) 
-      message = "Click to reattach the Applet!";
-    else message = "The was no applet load (maybe an error)!";
-
-    
-    int width = size().width / 2 - 
-      (getFontMetrics(getFont())).stringWidth(message) / 2;
-    int height = size().height / 2;
-    
-    g.setColor(Color.red);
-    g.drawString(message, width, height);
-  }
-  
-  /**
-   * reshape the applet and ourself
-   */
-  public void reshape(int x, int y, int w, int h)
-  {
-    if(applet != null) applet.reshape(x, y, w, h);
-    super.reshape(x, y, w, h);
-  }
-
-  /**
-   * Handle button events. When pressed it either creates the new applet
-   * window or destoys it.
-   */
-  public boolean handleEvent(Event evt) 
-  {
-    if(evt.target == startButton && evt.id == Event.ACTION_EVENT) {
-      if(applet == null) {
-        startButton.setLabel("Loading ...");
-        (loader = new Thread(this)).start();
-      } else {
-        if(applet.getParent() instanceof Frame) {
-          Frame frame = (Frame)applet.getParent();
-          frame.hide();
-          frame.dispose();
-        }
-        applet.stop();
-        applet.destroy();
-        applet = null;
-        startButton.setLabel(startLabel);
-      }
-      return true;
-    }
-    if(evt.id == Event.MOUSE_UP && applet.getParent() instanceof Frame) {
-      Frame frame = (Frame)applet.getParent();
-      frame.hide();
-      frame.dispose();
-      add("Center", applet);
-      validate();
-      layout();
-      return true;
-    }
-    return false;
-  }
-}
diff --git a/web/root/telnet/classes.zip b/web/root/telnet/classes.zip
deleted file mode 100644
index cb379d8a09a6b3f757679bfe247d1a8feb60d6c8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 85685
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

diff --git a/web/root/telnet/display/CharDisplay.class b/web/root/telnet/display/CharDisplay.class
deleted file mode 100644
index 6fa29a3fba83747ea12b7c9bf45fe67af7bf0985..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 19835
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
b+8J1xTfwG6@*Gm(1npllGOh-%%4-7vNU@;u

diff --git a/web/root/telnet/display/CharDisplay.java b/web/root/telnet/display/CharDisplay.java
deleted file mode 100644
index 9d3b32768e..0000000000
--- a/web/root/telnet/display/CharDisplay.java
+++ /dev/null
@@ -1,1297 +0,0 @@
-/**
- * CharDisplay -- a simple character display
- * --
- * $Id: CharDisplay.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Thu Jul 24 15:19:18 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package display;
-
-import java.awt.Graphics;
-import java.awt.Panel;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Point;
-import java.awt.Insets;
-import java.awt.Event;
-import java.awt.TextArea;
-import java.awt.Label;
-import java.awt.Frame;
-import java.awt.Scrollbar;
-import java.awt.Rectangle;
-
-/**
- * A simple character display.
- * @version $Id: CharDisplay.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- */
-public class CharDisplay extends Panel
-{
-  /**
-   * If you need the runtime version, just ask this variable.
-   */
-  public String version = "$Revision: 1.1.1.1 $ $Date: 2005/09/25 22:40:20 $";
-  /**
-   * Enable debug messages. This is final static to prevent unused
-   * code to be compiled.
-   */
-  public final static int debug = 0;
-  
-  private Dimension size;      /* rows and columns */
-  private Insets insets;      /* size of the border */
-  private boolean raised;      /* indicator if the border is raised */
-
-  private char charArray[][];      /* contains the characters */
-  private int charAttributes[][];    /* contains character attrs */
-  private int bufSize, maxBufSize;    /* buffer sizes */
-
-  private int windowBase;      /* where the start displaying */
-  private int screenBase;      /* the actual screen start */
-  private int topMargin;      /* top scroll margon */
-  private int bottomMargin;      /* bottom scroll margon */
-  private Scrollbar scrollBar;  /* the scroll bar */
-  private String scrBarPos;      /* the scroll bar position */
-
-  private Font normalFont;      /* normal font */
-  private FontMetrics fm;      /* current font metrics */
-  private int charWidth;      /* current width of a char */
-  private int charHeight;      /* current height of a char */
-  private int charDescent;      /* base line descent */
-  private int resizeStrategy;      /* current resizing strategy */
-
-  private int cursorX, cursorY;      /* current cursor position */
-  private Point selectBegin, selectEnd;  /* selection coordinates */
-  private TextArea selection;
-  private Frame selectFrame;
-
-  private display.SoftFont  sf = new display.SoftFont();
-
-  private boolean screenLocked = false;    /* screen needs to be locked */
-                                                /* because of paint requests */
-                                                /* during other operations */
-  private boolean update[];
-  
-  public Color notbold(Color colr)
-  {
-    return new Color(Math.max((int) (colr.getRed() *.85), 0),
-		Math.max((int) (colr.getGreen() * .85), 0),
-		Math.max((int) (colr.getBlue() * .85), 0));
-  }
-
-  private Color color[] = { notbold(Color.black),
-                            notbold(Color.red),
-                            notbold(Color.green),
-                            notbold(Color.yellow),
-                            notbold(Color.blue),
-                            notbold(Color.magenta),
-                            notbold(Color.cyan),
-                            notbold(Color.white),
-  };
-  private final static int COLOR_FG_STD = 7;
-  private final static int COLOR_FG_BOLD = 3;
-  private final static int COLOR_BG_STD = 0;
-  private final static int COLOR        = 0x7f8;
-  private final static int COLOR_FG     = 0x78;
-  private final static int COLOR_BG     = 0x780;
-
-  /**
-   * Scroll up when inserting a line.
-   */
-  public final static boolean SCROLL_UP   = false;
-  /**
-   * Scroll down when inserting a line.
-   */
-  public final static boolean SCROLL_DOWN = true;
-
-  /**
-   * Do nothing when the container is resized.
-   */
-  public final static int RESIZE_NONE  = 0;
-  /**
-   * Resize the width and height of the characterscreen.
-   */
-  public final static int RESIZE_SCREEN  = 1;
-  /**
-   * Resize the font to the new screensize.
-   */
-  public final static int RESIZE_FONT  = 2;
-  
-  /**
-   * Make character normal.
-   */ 
-  public final static int NORMAL  = 0x00;
-  /**
-   * Make character bold.
-   */ 
-  public final static int BOLD    = 0x01;
-  /**
-   * Underline character.
-   */ 
-  public final static int UNDERLINE  = 0x02;
-  /**
-   * Invert character.
-   */ 
-  public final static int INVERT  = 0x04;
-
-  private void InitializeCharDisplay(int width, int height, 
-                                     String fontname, int fsize)
-  {
-    System.err.println("CharDisplay: screen size: ["+width+","+height+"]");
-    normalFont = new Font(fontname, Font.BOLD, fsize);
-    setFont(normalFont);
-    fm = getFontMetrics(normalFont);
-    if(fm != null)
-    {
-      charWidth = fm.charWidth('@');
-      charHeight = fm.getHeight();
-      charDescent = fm.getDescent();
-    }
-
-    resizeStrategy = RESIZE_FONT;
-    size = new Dimension(width, height);
-    charArray = new char[size.height][size.width];
-    charAttributes = new int[size.height][size.width];
-    bufSize = size.height;
-    maxBufSize = 2 * size.height;
-
-    windowBase = 0;
-    screenBase = 0;
-    topMargin = 0;
-    bottomMargin = size.height - 1;
-
-    update = new boolean[size.height + 1];
-    for(int i = 1; i <= size.height; i++) update[i] = true;
-
-    selectBegin = new Point(0,0);
-    selectEnd = new Point(0,0);
-
-    setLayout(null);
-  }
-  
-  /**
-   * Create a character display with size 80x24 and Font "Courier", size 12.
-   */
-  public CharDisplay()
-  {
-    InitializeCharDisplay(80, 24, "Courier", 12);
-  }
-
-  /**
-   * Create a character display with specific size, Font is "Courier", size 12.
-   */
-  public CharDisplay(int width, int height)
-  {
-    InitializeCharDisplay(width, height, "Courier", 12);
-  }
-
-  /**
-   * Create a character display with 80x24 and specific font and font size.
-   */
-  public CharDisplay(String fname, int fsize)
-  {
-    InitializeCharDisplay(80, 24, fname, fsize);
-  }
-
-  /**
-   * Create a character display with specific size, font and font size.
-   */
-  public CharDisplay(int width, int height, String fname, int fsize)
-  {
-    InitializeCharDisplay(width, height, fname, fsize);
-  }
-  
-  /**
-   * Put a character on the screen with normal font and outline.
-   * The character previously on that position will be overwritten.
-   * You need to call redraw() to update the screen.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @param ch the character to show on the screen
-   * @see #insertChar
-   * @see #deleteChar
-   * @see #redraw
-   */
-  public void putChar(int c, int l, char ch)
-  {
-    putChar(c, l, ch, NORMAL);
-  }
-
-  /**
-   * Put a character on the screen with specific font and outline.
-   * The character previously on that position will be overwritten.
-   * You need to call redraw() to update the screen.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @param ch the character to show on the screen
-   * @param attributes the character attributes
-   * @see #BOLD
-   * @see #UNDERLINE
-   * @see #INVERT
-   * @see #NORMAL
-   * @see #insertChar
-   * @see #deleteChar
-   * @see #redraw
-   */  
-
-  public void putChar(int c, int l, char ch, int attributes)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-    charArray[screenBase + l][c] = ch;
-    charAttributes[screenBase + l][c] = attributes;
-    markLine(l, 1);
-  }
-
-  /**
-   * Get the character at the specified position.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @see #putChar
-   */
-  public char getChar(int c, int l)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-    return charArray[l][c];
-  }
-
-  /**
-   * Get the attributes for the specified position.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @see #putChar
-   */
-  public int getAttributes(int c, int l)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-    return charAttributes[l][c];
-  }
-
-  /**
-   * Insert a character at a specific position on the screen.
-   * All character right to from this position will be moved one to the right.
-   * You need to call redraw() to update the screen.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @param ch the character to insert
-   * @param attributes the character attributes
-   * @see #BOLD
-   * @see #UNDERLINE
-   * @see #INVERT
-   * @see #NORMAL
-   * @see #putChar
-   * @see #deleteChar
-   * @see #redraw
-   */
-  public void insertChar(int c, int l, char ch, int attributes)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-    System.arraycopy(charArray[screenBase + l], c, 
-         charArray[screenBase + l], c + 1, size.width - c - 1);
-    System.arraycopy(charAttributes[screenBase + l], c, 
-         charAttributes[screenBase + l], c + 1, size.width - c - 1);
-    putChar(c, l, ch, attributes);
-  }
-
-  /**
-   * Delete a character at a given position on the screen.
-   * All characters right to the position will be moved one to the left.
-   * You need to call redraw() to update the screen.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @see #putChar
-   * @see #insertChar
-   * @see #redraw
-   */
-  public void deleteChar(int c, int l)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-    if(c < size.width - 1)
-    {
-      System.arraycopy(charArray[screenBase + l], c + 1,
-           charArray[screenBase + l], c, size.width - c - 1);
-      System.arraycopy(charAttributes[screenBase + l], c + 1,
-           charAttributes[screenBase + l], c, size.width - c - 1);
-    }
-    putChar(size.width - 1, l, (char)0);
-  }
-
-  /**
-   * Put a String at a specific position. Any characters previously on that 
-   * position will be overwritten. You need to call redraw() for screen update.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @param s the string to be shown on the screen
-   * @see #BOLD
-   * @see #UNDERLINE
-   * @see #INVERT
-   * @see #NORMAL
-   * @see #putChar
-   * @see #insertLine
-   * @see #deleteLine
-   * @see #redraw
-   */  
-  public void putString(int c, int l, String s)
-  {
-    putString(c, l, s, NORMAL);
-  }
-  
-  /**
-   * Put a String at a specific position giving all characters the same
-   * attributes. Any characters previously on that position will be 
-   * overwritten. You need to call redraw() to update the screen.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (line)
-   * @param s the string to be shown on the screen
-   * @param attributes character attributes
-   * @see #BOLD
-   * @see #UNDERLINE
-   * @see #INVERT
-   * @see #NORMAL
-   * @see #putChar
-   * @see #insertLine
-   * @see #deleteLine
-   * @see #redraw
-   */
-  public void putString(int c, int l, String s, int attributes)
-  {
-    for(int i = 0; i < s.length() && c + i < size.width; i++)
-      putChar(c + i, l, s.charAt(i), attributes);
-  }
-
-  /**
-   * Insert a blank line at a specific position.
-   * The current line and all previous lines are scrolled one line up. The
-   * top line is lost. You need to call redraw() to update the screen.
-   * @param l the y-coordinate to insert the line
-   * @see #deleteLine
-   * @see #redraw
-   */
-  public void insertLine(int l)
-  {
-    insertLine(l, 1, SCROLL_UP);
-  }
-
-  /**
-   * Insert blank lines at a specific position.
-   * You need to call redraw() to update the screen
-   * @param l the y-coordinate to insert the line
-   * @param n amount of lines to be inserted
-   * @see #deleteLine
-   * @see #redraw
-   */
-  public void insertLine(int l, int n)
-  {
-    insertLine(l, n, SCROLL_UP);
-  }  
-
-  /**
-   * Insert a blank line at a specific position. Scroll text according to
-   * the argument.
-   * You need to call redraw() to update the screen
-   * @param l the y-coordinate to insert the line
-   * @param scrollDown scroll down
-   * @see #deleteLine
-   * @see #SCROLL_UP
-   * @see #SCROLL_DOWN
-   * @see #redraw
-   */
-  public void insertLine(int l, boolean scrollDown)
-  {
-    insertLine(l, 1, scrollDown);
-  }  
-
-  /**
-   * Insert blank lines at a specific position.
-   * The current line and all previous lines are scrolled one line up. The
-   * top line is lost. You need to call redraw() to update the screen.
-   * @param l the y-coordinate to insert the line
-   * @param n number of lines to be inserted
-   * @param scrollDown scroll down
-   * @see #deleteLine
-   * @see #SCROLL_UP
-   * @see #SCROLL_DOWN
-   * @see #redraw
-   */
-  public synchronized void insertLine(int l, int n, boolean scrollDown)
-  {
-    screenLocked = true;
-
-    l = checkBounds(l, 0, size.height - 1);
-
-    char cbuf[][] = null;
-    int abuf[][] = null;
-    int offset = 0;
-    int oldBase = screenBase;
-    int top = (l < topMargin ? 
-               0 : (l > bottomMargin ?
-                    (bottomMargin + 1 < size.height ?
-                     bottomMargin + 1 : size.height - 1) : topMargin));
-    int bottom = (l > bottomMargin ?
-                  size.height - 1 : (l < topMargin ? 
-                                     (topMargin > 0 ?
-                                      topMargin - 1 : 0) : bottomMargin));
-    
-    
-    if(scrollDown) {
-      if(n > (bottom - top)) n = (bottom - top);
-      cbuf = new char[bottom - l - (n - 1)][size.width];
-      abuf = new int[bottom - l - (n - 1)][size.width];
-      
-      System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l - (n - 1));
-      System.arraycopy(charAttributes, oldBase + l, 
-		       abuf, 0, bottom - l - (n - 1));
-      System.arraycopy(cbuf, 0, charArray, oldBase + l + n, 
-		       bottom - l - (n - 1));
-      System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, 
-		       bottom - l - (n - 1));
-      cbuf = charArray;
-      abuf = charAttributes;
-    } else try {
-      if(n > (bottom - top) + 1) n = (bottom - top) + 1;
-      if(bufSize < maxBufSize) {
-        if(bufSize + n > maxBufSize) {
-          offset = n - (maxBufSize - bufSize);
-          bufSize = maxBufSize;
-          screenBase = maxBufSize - size.height - 1;
-          windowBase = screenBase;
-        } else {
-          screenBase += n;
-          windowBase += n;
-          bufSize += n;
-        }
-        cbuf = new char[bufSize][size.width];
-        abuf = new int[bufSize][size.width];
-      } else {
-        offset = n;
-        cbuf = charArray;
-        abuf = charAttributes;
-      }
-      /*
-       * copy anything from the top of the buffer (+offset) to the new top
-       * up to the screenBase.
-       */
-      if(oldBase > 0)
-      {
-        System.arraycopy(charArray, offset, 
-                         cbuf, 0, 
-                         oldBase - offset);
-        System.arraycopy(charAttributes, offset, 
-                         abuf, 0, 
-                         oldBase - offset);
-      }
-      /*
-       * copy anything from the top of the screen (screenBase) up to the
-       * topMargin to the new screen
-       */
-      if(top > 0)
-      {
-        System.arraycopy(charArray, oldBase, 
-                         cbuf, screenBase, 
-                         top);
-        System.arraycopy(charAttributes, oldBase, 
-                         abuf, screenBase, 
-                         top);
-      }
-      /* 
-       * copy anything from the topMargin up to the amount of lines inserted
-       * to the gap left over between scrollback buffer and screenBase
-       */
-      if(oldBase > 0) {
-	System.arraycopy(charArray, oldBase + top, 
-			 cbuf, oldBase - offset,
-			 n);
-	System.arraycopy(charAttributes, oldBase + top, 
-			 abuf, oldBase - offset,
-			 n);
-      }
-      /*
-       * copy anything from topMargin + n up to the line linserted to the
-       * topMargin
-       */
-      System.arraycopy(charArray, oldBase + top + n,
-                       cbuf, screenBase + top,
-                       l - top - (n - 1));
-      System.arraycopy(charAttributes, oldBase + top + n,
-                       abuf, screenBase + top,
-                       l - top - (n - 1));
-      /*
-       * copy the all lines next to the inserted to the new buffer
-       */
-      if(l < size.height - 1)
-      {
-        System.arraycopy(charArray, oldBase + l + 1,
-                         cbuf, screenBase + l + 1,
-                         (size.height - 1) - l);
-        System.arraycopy(charAttributes, oldBase + l + 1,
-                         abuf, screenBase + l + 1,
-                         (size.height - 1) - l);
-      }
-    } catch(ArrayIndexOutOfBoundsException e) {
-      System.err.println("*** Error while scrolling up:");
-      System.err.println("--- BEGIN STACKTRACE ---");
-      e.printStackTrace();
-      System.err.println("--- END STACKTRACE ---");
-      System.err.println("bufSize="+bufSize+", maxBufSize="+maxBufSize);
-      System.err.println("top="+top+", bottom="+bottom);
-      System.err.println("n="+n+", l="+l);
-      System.err.println("screenBase="+screenBase+", windowBase="+windowBase);
-      System.err.println("oldBase="+oldBase);
-      System.err.println("size.width="+size.width+", size.height="+size.height);
-      System.err.println("abuf.length="+abuf.length+", cbuf.length="+cbuf.length);
-      System.err.println("*** done dumping debug information");
-    }
-    
-    for(int i = 0; i < n; i++)
-    {
-      cbuf[(screenBase + l) + (scrollDown ? i : -i) ] = new char[size.width];
-      abuf[(screenBase + l) + (scrollDown ? i : -i) ] = new int[size.width];
-    }
-
-    charArray = cbuf;
-    charAttributes = abuf;
-    
-    if(scrollDown)
-      markLine(l, bottom - l + 1);
-    else
-      markLine(top, l - top + 1);
-
-    if(scrollBar != null)
-      scrollBar.setValues(windowBase, size.height, 0, bufSize);
-    
-    screenLocked = false;
-  }
-  
-  /**
-   * Delete a line at a specific position. Subsequent lines will be scrolled 
-   * up to fill the space and a blank line is inserted at the end of the 
-   * screen.
-   * @param l the y-coordinate to insert the line
-   * @see #deleteLine
-   */
-  public void deleteLine(int l)
-  {
-    l = checkBounds(l, 0, size.height - 1);
-
-    int bottom = (l>bottomMargin?size.height-1:
-		  (l<topMargin?topMargin:bottomMargin+1));
-    System.arraycopy(charArray, screenBase + l + 1,
-                     charArray, screenBase + l, bottom - l -1 );
-    System.arraycopy(charAttributes, screenBase + l + 1,
-                     charAttributes, screenBase + l, bottom - l -1);
-    charArray[screenBase + bottom - 1] = new char[size.width];
-    charAttributes[screenBase + bottom - 1] = new int[size.width];
-    markLine(l, bottom - l);
-  }
-
-
-  /**
-   * Delete a rectangular portion of the screen.
-   * You need to call redraw() to update the screen.
-   * @param c x-coordinate (column)
-   * @param l y-coordinate (row)
-   * @param w with of the area in characters
-   * @param h height of the area in characters
-   * @see #deleteChar
-   * @see #deleteLine
-   * @see redraw
-   */
-  public void deleteArea(int c, int l, int w, int h)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-
-    char cbuf[] = new char[w];
-    int abuf[] = new int[w];
-    
-    for(int i = 0; i < h && l + i < size.height; i++)
-    {
-      System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w);
-      System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w);
-    }
-    markLine(l, h);
-  }
-
-  /**
-   * Puts the cursor at the specified position.
-   * @param c column
-   * @param l line
-   */
-  public void setCursorPos(int c, int l)
-  {
-    c = checkBounds(c, 0, size.width - 1);
-    l = checkBounds(l, 0, size.height - 1);
-    markLine(cursorY, 1);
-    cursorX = (c < size.width ? c : size.width);
-    cursorY = (l < size.height ? l : size.height);
-    markLine(l, 1);
-  }
-
-  /**
-   * Get the current cursor position.
-   * @see java.awt.Dimension
-   */
-  public Dimension getCursorPos()
-  {
-    return new Dimension(cursorX, cursorY);
-  }
-
-  /**
-   * Set the top scroll margin for the screen. If the current bottom margin
-   * is smaller it will become the top margin and the line will become the
-   * bottom margin.
-   * @param l line that is the margin
-   */
-  public void setTopMargin(int l)
-  {
-    if(l > bottomMargin) 
-    {
-      topMargin = bottomMargin;
-      bottomMargin = l;
-    }
-    else
-      topMargin = l;
-    if(topMargin < 0) topMargin = 0;
-    if(bottomMargin > size.height - 1) bottomMargin = size.height - 1;
-  }
-
-  /**
-   * Get the top scroll margin.
-   */
-  public int getTopMargin()
-  {
-    return topMargin;
-  }
-
-  /**
-   * Set the bottom scroll margin for the screen. If the current top margin
-   * is bigger it will become the bottom margin and the line will become the
-   * top margin.
-   * @param l line that is the margin
-   */
-  public void setBottomMargin(int l)
-  {
-    if(l < topMargin) 
-    {
-      bottomMargin = topMargin;
-      topMargin = l;
-    }
-    else
-      bottomMargin = l;
-    if(topMargin < 0) topMargin = 0;
-    if(bottomMargin > size.height - 1) bottomMargin = size.height - 1;
-  }
-
-  /**
-   * Get the bottom scroll margin.
-   */
-  public int getBottomMargin()
-  {
-    return bottomMargin;
-  }
-    
-  /**
-   * Set scrollback buffer size.
-   * @param amount new size of the buffer
-   */
-  public void setBufferSize(int amount)
-  {
-    screenLocked = true;
-
-    if(amount < size.height) amount = size.height;
-    if(amount < maxBufSize)
-    {
-      char cbuf[][] = new char[amount][size.width];
-      int abuf[][] = new int[amount][size.width];
-      System.arraycopy(charArray, bufSize - amount, cbuf, 0, amount);
-      System.arraycopy(charAttributes, bufSize - amount, abuf, 0, amount);
-      charArray = cbuf;
-      charAttributes = abuf;
-    }
-    maxBufSize = amount;
- 
-    screenLocked = false;
-
-    repaint();
-  }
-
-  /**
-   * Retrieve current scrollback buffer size.
-   * @see #setBufferSize
-   */
-  public int getBufferSize()
-  {
-    return bufSize;
-  }
-
-  /**
-   * Retrieve maximum buffer Size.
-   * @see #getBufferSize
-   */
-  public int getMaxBufferSize()
-  {
-    return maxBufSize;
-  }
-
-  /**
-   * Set the current window base. This allows to view the scrollback buffer.
-   * @param line the line where the screen window starts
-   * @see setBufferSize
-   * @see getBufferSize
-   */
-  public void setWindowBase(int line)
-  {
-    if(line > screenBase) line = screenBase;
-    else if(line < 0) line = 0;
-    windowBase = line;
-    repaint();
-  }
-
-  /**
-   * Get the current window base.
-   * @see setWindowBase
-   */
-  public int getWindowBase()
-  {
-    return windowBase;
-  }
-
-  /**
-   * Change the size of the screen. This will include adjustment of the 
-   * scrollback buffer.
-   * @param columns width of the screen
-   * @param columns height of the screen
-   */
-  public void setWindowSize(int width, int height)
-  {
-    char cbuf[][];
-    int abuf[][];
-    int bsize = bufSize;
-
-    if(width < 1 || height < 1) return;
-
-    screenLocked = true;
-    
-    super.update(getGraphics());
-    
-    if(height > maxBufSize) 
-      maxBufSize = height;
-    if(height > bufSize)
-    {
-      bufSize = height;
-      screenBase = 0;
-      windowBase = 0;
-    }
-
-    cbuf = new char[bufSize][width];
-    abuf = new int[bufSize][width];
-    
-    for(int i = 0; i < bsize && i < bufSize; i++)
-    {
-      System.arraycopy(charArray[i], 0, cbuf[i], 0, 
-           width < size.width ? width : size.width);
-      System.arraycopy(charAttributes[i], 0, abuf[i], 0, 
-           width < size.width ? width : size.width);
-    }
-    charArray = cbuf;
-    charAttributes = abuf;
-    size = new Dimension(width, height);
-    topMargin = 0;
-    bottomMargin = height - 1;
-    update = new boolean[height + 1];
-    for(int i = 0; i <= height; i++) update[i] = true;
-    screenLocked = false;
-  }
-
-  /**
-   * Set the strategy when window is resized.
-   * RESIZE_FONT is default.
-   * @param strategy the strategy
-   * @see #RESIZE_NONE
-   * @see #RESIZE_FONT
-   * @see #RESIZE_SCREEN
-   */
-  public void setResizeStrategy(int strategy)
-  {
-    resizeStrategy = strategy;
-  }
-  
-  /**
-   * Get amount of rows on the screen.
-   */
-  public int getRows() { return size.height; }
-
-  /**
-   * Get amount of columns on the screen.
-   */
-  public int getColumns() { return size.width; }
-
-  /**
-   * Set the border thickness and the border type.
-   * @param thickness border thickness in pixels, zero means no border
-   * @param raised a boolean indicating a raised or embossed border
-   */
-  public void setBorder(int thickness, boolean raised)
-  {
-    if(thickness == 0) insets = null;
-    else insets = new Insets(thickness+1, thickness+1, 
-                             thickness+1, thickness+1);
-    this.raised = raised;
-  }
-
-  /**
-   * Set the scrollbar position. valid values are "East" or "West".
-   * @param position the position of the scrollbar
-   */
-  public void setScrollbar(String position)
-  {
-    add(scrollBar = new Scrollbar());
-    scrollBar.setValues(windowBase, size.height, 0, bufSize - size.height);
-    scrBarPos = position;
-  }
-  
-  /**
-   * Mark lines to be updated with redraw().
-   * @param l starting line
-   * @param n amount of lines to be updated
-   * @see #redraw
-   */
-  public void markLine(int l, int n)
-  {
-    l = checkBounds(l, 0, size.height - 1);
-    for(int i = 0; i < n && l + i < size.height; i++) 
-      update[l + i + 1] = true;
-  }
-  
-  /**
-   * Redraw marked lines.
-   * @see #markLine
-   */
-  public void redraw()
-  {
-    update[0] = true;
-    repaint();
-  }
-
-  /**
-   * Update the display. to reduce flashing we have overridden this method.
-   */
-  public void update(Graphics g)
-  {
-    paint(g);
-  }
-  
-  /**
-   * Paint the current screen. All painting is done here. Only lines that have
-   * changed will be redrawn!
-   */
-  public synchronized void paint(Graphics g)
-  {
-    if(screenLocked) return;
-    int xoffset = (super.size().width - size.width * charWidth - 
-                   (scrollBar != null ? 15 : 0)) / 2;
-    int yoffset = (super.size().height - size.height * charHeight) / 2;
-
-    Color fg = color[COLOR_FG_STD];
-    Color bg = color[COLOR_BG_STD];
-
-    if(scrollBar != null && scrBarPos.equals("West")) xoffset += 15;
-    
-    g.setFont(normalFont);
-
-    for(int l = 0; l < size.height; l++)
-    {
-      if(update[0] && !update[l + 1]) continue;
-      update[l + 1] = false;
-      for(int c = 0; c < size.width; c++)
-      {
-        int addr = 0;
-        int currAttr = charAttributes[windowBase + l][c];
-
-        fg = color[COLOR_FG_STD];
-    	bg = color[COLOR_BG_STD];
-
-	// Special handling of BOLD for terminals used on 5ESS 
-        if(((currAttr & BOLD) != 0)   &&
-	   ((currAttr & COLOR_FG) == 0) &&
-	   ((currAttr & COLOR_BG) == 0)   )
-	{
-	  fg = color[COLOR_FG_BOLD];
-	}
-
-        if ((currAttr & COLOR_FG) != 0) {
-          fg = color[((currAttr & COLOR_FG) >> 3)-1];
-        }
-        if ((currAttr & COLOR_BG) != 0) {
-          bg = color[((currAttr & COLOR_BG) >> 7)-1];
-        }
-
-        if((currAttr & BOLD) != 0) {
-          if(fg.equals(Color.black))
-            fg = Color.gray;
-          else {
-	    fg = fg.brighter();
-	    bg = bg.brighter();
-	  }
-        }
-        if((currAttr & INVERT) != 0) { Color swapc = bg; bg=fg;fg=swapc; }
-
-        if (sf.inSoftFont(charArray[windowBase + l][c])) {
-          g.setColor(bg);	
-          g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset, 
-		     charWidth, charHeight);
-          g.setColor(fg);	
-          sf.drawChar(g,charArray[windowBase + l][c],xoffset+c*charWidth,
-		      l*charHeight+yoffset, charWidth, charHeight);
-          if((currAttr & UNDERLINE) != 0)
-            g.drawLine(c * charWidth + xoffset,
-                     (l+1) * charHeight - charDescent / 2 + yoffset,
-                     c * charWidth + charWidth + xoffset, 
-                     (l+1) * charHeight - charDescent / 2 + yoffset);
-          continue;
-        }
-        
-	// determine the maximum of characters we can print in one go
-        while(c + addr < size.width && 
-              charAttributes[windowBase + l][c + addr] == currAttr &&
-              !sf.inSoftFont(charArray[windowBase + l ][c+addr])
-        ) {
-          if(charArray[windowBase + l][c + addr] < ' ')
-            charArray[windowBase + l][c + addr] = ' ';
-          addr++;
-        }
-        
-        // clear the part of the screen we want to change (fill rectangle)
-        g.setColor(bg);
-        g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset,
-                   addr * charWidth, charHeight);
-
-        g.setColor(fg);
-        
-	// draw the characters
-        g.drawChars(charArray[windowBase + l], c, addr, 
-                    c * charWidth + xoffset, 
-                    (l+1) * charHeight - charDescent + yoffset);
-
-        if((currAttr & UNDERLINE) != 0)
-          g.drawLine(c * charWidth + xoffset,
-                     (l+1) * charHeight - charDescent / 2 + yoffset,
-                     c * charWidth + addr * charWidth + xoffset, 
-                     (l+1) * charHeight - charDescent / 2 + yoffset);
-        
-        c += addr - 1;
-      }
-    }
-
-    // draw cursor
-    if(screenBase + cursorY >= windowBase && 
-       screenBase + cursorY < windowBase + size.height)
-    {
-      g.setColor(color[COLOR_FG_STD]);
-      g.setXORMode(color[COLOR_BG_STD]);
-      g.fillRect( cursorX * charWidth + xoffset, 
-                 (cursorY + screenBase - windowBase) * charHeight + yoffset,
-                 charWidth, charHeight);
-      g.setPaintMode();
-    }
-
-    if(windowBase <= selectBegin.y || windowBase <= selectEnd.y) {
-      int beginLine = selectBegin.y - windowBase;
-      int endLine = selectEnd.y - selectBegin.y;
-      if(beginLine < 0) {
-        endLine += beginLine;
-        beginLine = 0;
-      }
-      if(endLine > size.height) endLine = size.height - beginLine;
-       
-      g.setXORMode(color[COLOR_BG_STD]);
-      g.fillRect(selectBegin.x * charWidth + xoffset,
-                 beginLine * charHeight + yoffset,
-                 (endLine == 0 ? (selectEnd.x - selectBegin.x) : 
-                  (size.width - selectBegin.x)) 
-                 * charWidth,
-                 charHeight);
-      if(endLine > 1)
-        g.fillRect(0 + xoffset, 
-                   (beginLine + 1) * charHeight + yoffset, 
-                   size.width * charWidth, 
-                   (endLine - 1) * charHeight);
-      if(endLine > 0)
-        g.fillRect(0 + xoffset, 
-                   (beginLine + endLine) * charHeight + yoffset,
-                   selectEnd.x * charWidth, 
-                   charHeight);
-      g.setPaintMode();
-    }
-
-    if(insets != null) {
-      g.setColor(getBackground());
-      xoffset--; yoffset--;
-      for(int i = insets.top - 1; i >= 0; i--)
-        g.draw3DRect(xoffset - i, yoffset - i,
-                     charWidth * size.width + 1 + i * 2, 
-                     charHeight * size.height + 1 + i * 2,
-                     raised);
-    }
-
-    update[0] = false;
-  }
-
-  private int checkBounds(int value, int lower, int upper)
-  {
-    if(value < lower) return lower;
-    if(value > upper) return upper;
-    return value;
-  }
-
-  /**
-   * Reshape character display according to resize strategy.
-   * @see #setResizeStrategy
-   */
-  public void reshape(int x, int y, int w, int h)
-  {
-    if(debug > 0)
-      System.err.println("CharDisplay: reshape("+x+","+y+","+w+","+h+")");
-
-    int xborder = 0, yborder = 0;
-    
-    if(insets != null) {
-      w -= (xborder = insets.left + insets.right);
-      h -= (yborder = insets.top + insets.bottom);
-    }
-    if(scrollBar != null) { w -= 15;}
-
-    Font tmpFont = normalFont;
-    String fontName = normalFont.getName();
-    fm = getFontMetrics(normalFont);
-    if(fm != null)
-    {
-      charWidth = fm.charWidth('@');
-      charHeight = fm.getHeight();
-    }
-    
-    switch(resizeStrategy)
-    {
-    case RESIZE_SCREEN:
-      setWindowSize(w / charWidth, size.height = h / charHeight);
-      break;
-    case RESIZE_FONT:
-      int height = h / size.height;
-      int width = w / size.width;
-      
-      fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
-                                                charHeight));
-      
-      // adapt current font size (from small up to best fit)
-      if(fm.getHeight() < height || fm.charWidth('@') < width)
-        do {
-          fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
-                                                    ++charHeight));
-        } while(fm.getHeight() < height || 
-                fm.charWidth('@') < width); 
-      
-      // now check if we got a font that is too large
-      if(fm.getHeight() > height || fm.charWidth('@') > width)
-        do {
-          fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
-                                                    --charHeight));
-        } while(charHeight > 1 && 
-                (fm.getHeight() > height || 
-                 fm.charWidth('@') > width));
-      
-      if(charHeight <= 1) 
-      {
-        System.err.println("CharDisplay: error during resize, resetting");
-        normalFont = tmpFont;
-        System.err.println("CharDisplay: disabling font/screen resize");
-        resizeStrategy = RESIZE_NONE;
-      }
-
-      setFont(normalFont);
-      fm = getFontMetrics(normalFont);
-      charWidth = fm.charWidth('@');
-      charHeight = fm.getHeight();
-      charDescent = fm.getDescent();
-      break;
-    case RESIZE_NONE:
-    default:
-      break;
-    }
-    if(debug > 0)
-    {
-      System.err.println("CharDisplay: charWidth="+charWidth+", "+
-                         "charHeight="+charHeight+", "+
-                         "charDescent="+charDescent);
-      System.err.println("CharDisplay: "+normalFont+", "+fm);
-    }
-    super.reshape(x, y, 
-                  w + xborder + (scrollBar != null ? 15 : 0), 
-                  h + yborder);
-    
-    if(scrollBar != null) {
-      int xoffset = (super.size().width - size.width * charWidth - 15) / 2;
-      int yoffset = (super.size().height - size.height * charHeight) / 2;
-      if(scrBarPos.equals("West"))
-        scrollBar.reshape(xoffset - (xborder / 2), yoffset - yborder / 2,
-                          15, size.height * charHeight + yborder);
-      else
-        scrollBar.reshape(xoffset + (xborder / 2) + size.width * charWidth, 
-                          yoffset - yborder / 2, 15, 
-                          size.height * charHeight + yborder);
-    }
-  }
-
-  /**
-   * Return the real size in points of the character display.
-   * @return Dimension the dimension of the display
-   * @see java.awt.Dimension
-   */
-  public Dimension size()
-  {
-    int xborder = 0, yborder = 0;
-    if(insets != null) {
-      xborder = insets.left + insets.right;
-      yborder = insets.top + insets.bottom;
-    }
-    if(scrollBar != null) xborder += 15;
-    
-    return new Dimension(size.width * charWidth + xborder, 
-                         size.height * charHeight + yborder);
-  }
-
-  /**
-   * Return the preferred Size of the character display.
-   * This turns out to be the actual size.
-   * @return Dimension dimension of the display
-   * @see size
-   */
-  public Dimension preferredSize() 
-  {
-    return size();
-  }
-
-  /**
-   * The insets of the character display define the border.
-   * @return Insets border thickness in pixels
-   */
-  public Insets insets() 
-  {
-    return insets == null ? super.insets() : insets;
-  }
-
-  /**
-   * Handle mouse events for copy & paste
-   * @param evt the event that occured
-   * @return boolean true if action was taken
-   * @see java.awt.Event
-   */
-  public boolean handleEvent(Event evt) 
-  {
-    // handle scrollbar events
-    if(evt != null && evt.target == scrollBar && evt.arg != null) {
-      int val = ((Integer)evt.arg).intValue();
-      setWindowBase(val);
-      return true;
-    }
-
-    if(evt.id == Event.MOUSE_DOWN || evt.id == Event.MOUSE_UP ||
-       evt.id == Event.MOUSE_DRAG) {
-      int xoffset = (super.size().width - size.width * charWidth) / 2;
-      int yoffset = (super.size().height - size.height * charHeight) / 2;
-      switch(evt.id) {
-      case Event.MOUSE_DOWN:
-        selectBegin.x = (evt.x - xoffset) / charWidth;
-        selectBegin.y = (evt.y - yoffset) / charHeight + windowBase;
-        selectEnd.x = selectBegin.x;
-        selectEnd.y = selectBegin.y;
-        if(selectFrame != null) selectFrame.hide();
-        break;
-      case Event.MOUSE_UP:
-      case Event.MOUSE_DRAG:
-        int x = (evt.x - xoffset) / charWidth;
-        int y = (evt.y - yoffset) / charHeight + windowBase;
-        int oldx = selectEnd.x, oldy = selectEnd.y;
-
-        if((x < selectBegin.x && y < selectBegin.y) &&
-           (x < selectEnd.x && y < selectEnd.y)) {
-          selectBegin.x = x;
-          selectBegin.y = y;
-        } else {
-          selectEnd.x = x;
-          selectEnd.y = y;
-        }
-
-        if(evt.id == Event.MOUSE_UP) {
-          if(selectBegin.x == selectEnd.x &&
-             selectBegin.y == selectEnd.y) {
-            repaint();
-            return true;
-          }
-          String tmp = "";
-	  // fix end.x and end.y, they can get over the border
-	  if (selectEnd.x < 0) selectEnd.x = 0;
-	  if (selectEnd.y < 0) selectEnd.y = 0;
-	  if (selectEnd.y >= charArray.length) {
-		selectEnd.y = charArray.length-1;
-	  }
-	  if (selectEnd.x >= charArray[0].length) {
-		selectEnd.x = charArray[0].length-1;
-	  }
-          for(int l = selectBegin.y; l <= selectEnd.y; l++)
-            if(l == selectBegin.y) 
-              tmp = (new String(charArray[l])).substring(selectBegin.x) + "\n";
-            else if(l == selectEnd.y) 
-              tmp += (new String(charArray[l])).substring(0, selectEnd.x);
-            else tmp += new String(charArray[l]) + "\n";
-          if(selectFrame == null) {
-
-	    /* for jdk-1.1
-	    String s=(String) ((StringSelection)this.getToolkit().
-			       getSystemClipboard().
-			       getContents(this)).
-	      getTransferData(DataFlavor.stringFlavor);
-	    System.out.println(s);
-	    */
-            selectFrame = new Frame("Pasteboard");
-            selection = new TextArea();
-            selection.setFont(normalFont);
-            selectFrame.add("Center", selection);
-            selectFrame.add("South", new Label("Click on the terminal window"+
-                                               " to hide!"));
-            selectFrame.pack();
-          }
-          selection.setText(tmp);
-          selectFrame.show();
-          selection.selectAll();
-          repaint();
-        } else
-          if(oldx != x || oldy != y) repaint();
-        break;
-      }
-      return true;
-    }
-    return false;
-  }
-}
diff --git a/web/root/telnet/display/SoftFont.class b/web/root/telnet/display/SoftFont.class
deleted file mode 100644
index 327e2572416d75f327814f185071c176d60282d3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 10793
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

diff --git a/web/root/telnet/display/SoftFont.java b/web/root/telnet/display/SoftFont.java
deleted file mode 100644
index 961799049c..0000000000
--- a/web/root/telnet/display/SoftFont.java
+++ /dev/null
@@ -1,1005 +0,0 @@
-/*
- * SoftFont -- a unicode softfont displayer
- * --
- * $Id: SoftFont.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-package display;
-
-import java.awt.*;
-import java.util.*;
-/**********************************************/
-/*                                            */
-/*       Font file generated by cpi2fnt       */
-/*                                            */
-/**********************************************/
-
-public class SoftFont {
-	final static private char	SF_BITMAP = 0;
-	final static private char	SF_FILLRECT = 1;
-
-
-	final static private char	SF_CHAR	= 0;
-	final static private char	SF_WIDTH= 1;
-	final static private char	SF_HEIGHT= 2;
-	final static private char	SF_TYPE  = 3;
-	final static private char	SF_DATA  = 4;
-	java.util.Hashtable font;
-	/** 
-	 * softfont characterdata
-	 */
-	private static char[][] fontdata = {
-	
-	{0x01,8,8,SF_BITMAP, /* 1 0x01 '^A' */
-	0x7e, /* 01111110 */
-	0x81, /* 10000001 */
-	0xa5, /* 10100101 */
-	0x81, /* 10000001 */
-	0xbd, /* 10111101 */
-	0x99, /* 10011001 */
-	0x81, /* 10000001 */
-	0x7e, /* 01111110 */
-	},{ 0x02,8,8,SF_BITMAP,/* 2 0x02 '^B' */
-	0x7e, /* 01111110 */
-	0xff, /* 11111111 */
-	0xdb, /* 11011011 */
-	0xff, /* 11111111 */
-	0xc3, /* 11000011 */
-	0xe7, /* 11100111 */
-	0xff, /* 11111111 */
-	0x7e, /* 01111110 */
-	},{ 0x03,8,8,SF_BITMAP,/* 3 0x03 '^C' */
-	0x6c, /* 01101100 */
-	0xfe, /* 11111110 */
-	0xfe, /* 11111110 */
-	0xfe, /* 11111110 */
-	0x7c, /* 01111100 */
-	0x38, /* 00111000 */
-	0x10, /* 00010000 */
-	0x00, /* 00000000 */
-	},{ 0x04,8,8,SF_BITMAP,/* 4 0x04 '^D' */
-	0x10, /* 00010000 */
-	0x38, /* 00111000 */
-	0x7c, /* 01111100 */
-	0xfe, /* 11111110 */
-	0x7c, /* 01111100 */
-	0x38, /* 00111000 */
-	0x10, /* 00010000 */
-	0x00, /* 00000000 */
-	},{ 0x05,8,8,SF_BITMAP,/* 5 0x05 '^E' */
-	0x38, /* 00111000 */
-	0x7c, /* 01111100 */
-	0x38, /* 00111000 */
-	0xfe, /* 11111110 */
-	0xfe, /* 11111110 */
-	0xd6, /* 11010110 */
-	0x10, /* 00010000 */
-	0x38, /* 00111000 */
-	},{ 0x06,8,8,SF_BITMAP,/* 6 0x06 '^F' */
-	0x10, /* 00010000 */
-	0x38, /* 00111000 */
-	0x7c, /* 01111100 */
-	0xfe, /* 11111110 */
-	0xfe, /* 11111110 */
-	0x7c, /* 01111100 */
-	0x10, /* 00010000 */
-	0x38, /* 00111000 */
-	},{ 0x2666,8,8,SF_BITMAP,/* 9830 0x2666 BLACK DIAMOND */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0x18, /* 00011000 */
-	0x3c, /* 00111100 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x07,8,8,SF_BITMAP,/* 7 0x07 '^G' */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0x18, /* 00011000 */
-	0x3c, /* 00111100 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x08,8,8,SF_BITMAP,/* 8 0x08 '^H' */
-	0xff, /* 11111111 */
-	0xff, /* 11111111 */
-	0xe7, /* 11100111 */
-	0xc3, /* 11000011 */
-	0xc3, /* 11000011 */
-	0xe7, /* 11100111 */
-	0xff, /* 11111111 */
-	0xff, /* 11111111 */
-	},{ 0x09,8,8,SF_BITMAP,/* 9 0x09 '^I' */
-	0x00, /* 00000000 */
-	0x3c, /* 00111100 */
-	0x66, /* 01100110 */
-	0x42, /* 01000010 */
-	0x42, /* 01000010 */
-	0x66, /* 01100110 */
-	0x3c, /* 00111100 */
-	0x00, /* 00000000 */
-	},{ 0x0a,8,8,SF_BITMAP,/* 10 0x0a '^J' */
-	0xff, /* 11111111 */
-	0xc3, /* 11000011 */
-	0x99, /* 10011001 */
-	0xbd, /* 10111101 */
-	0xbd, /* 10111101 */
-	0x99, /* 10011001 */
-	0xc3, /* 11000011 */
-	0xff, /* 11111111 */
-	},{ 0x0b,8,8,SF_BITMAP,/* 11 0x0b '^K' */
-	0x0f, /* 00001111 */
-	0x07, /* 00000111 */
-	0x0f, /* 00001111 */
-	0x7d, /* 01111101 */
-	0xcc, /* 11001100 */
-	0xcc, /* 11001100 */
-	0xcc, /* 11001100 */
-	0x78, /* 01111000 */
-	},{ 0x0c,8,8,SF_BITMAP,/* 12 0x0c '^L' */
-	0x3c, /* 00111100 */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	0x7e, /* 01111110 */
-	0x18, /* 00011000 */
-	},{ 0x0d,8,8,SF_BITMAP,/* 13 0x0d '^M' */
-	0x3f, /* 00111111 */
-	0x33, /* 00110011 */
-	0x3f, /* 00111111 */
-	0x30, /* 00110000 */
-	0x30, /* 00110000 */
-	0x70, /* 01110000 */
-	0xf0, /* 11110000 */
-	0xe0, /* 11100000 */
-	},{ 0x0e,8,8,SF_BITMAP,/* 14 0x0e '^N' */
-	0x7f, /* 01111111 */
-	0x63, /* 01100011 */
-	0x7f, /* 01111111 */
-	0x63, /* 01100011 */
-	0x63, /* 01100011 */
-	0x67, /* 01100111 */
-	0xe6, /* 11100110 */
-	0xc0, /* 11000000 */
-	},{ 0x0f,8,8,SF_BITMAP,/* 15 0x0f '^O' */
-	0x18, /* 00011000 */
-	0xdb, /* 11011011 */
-	0x3c, /* 00111100 */
-	0xe7, /* 11100111 */
-	0xe7, /* 11100111 */
-	0x3c, /* 00111100 */
-	0xdb, /* 11011011 */
-	0x18, /* 00011000 */
-	},{ 0x10,8,8,SF_BITMAP,/* 16 0x10 '^P' */
-	0x80, /* 10000000 */
-	0xe0, /* 11100000 */
-	0xf8, /* 11111000 */
-	0xfe, /* 11111110 */
-	0xf8, /* 11111000 */
-	0xe0, /* 11100000 */
-	0x80, /* 10000000 */
-	0x00, /* 00000000 */
-	},{ 0x11,8,8,SF_BITMAP,/* 17 0x11 '^Q' */
-	0x02, /* 00000010 */
-	0x0e, /* 00001110 */
-	0x3e, /* 00111110 */
-	0xfe, /* 11111110 */
-	0x3e, /* 00111110 */
-	0x0e, /* 00001110 */
-	0x02, /* 00000010 */
-	0x00, /* 00000000 */
-	},{ 0x12,8,8,SF_BITMAP,/* 18 0x12 '^R' */
-	0x18, /* 00011000 */
-	0x3c, /* 00111100 */
-	0x7e, /* 01111110 */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x7e, /* 01111110 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	},{ 0x13,8,8,SF_BITMAP,/* 19 0x13 '^S' */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x00, /* 00000000 */
-	0x66, /* 01100110 */
-	0x00, /* 00000000 */
-	},{ 0x14,8,8,SF_BITMAP,/* 20 0x14 '^T' */
-	0x7f, /* 01111111 */
-	0xdb, /* 11011011 */
-	0xdb, /* 11011011 */
-	0x7b, /* 01111011 */
-	0x1b, /* 00011011 */
-	0x1b, /* 00011011 */
-	0x1b, /* 00011011 */
-	0x00, /* 00000000 */
-	},{ 0x15,8,8,SF_BITMAP,/* 21 0x15 '^U' */
-	0x3e, /* 00111110 */
-	0x61, /* 01100001 */
-	0x3c, /* 00111100 */
-	0x66, /* 01100110 */
-	0x66, /* 01100110 */
-	0x3c, /* 00111100 */
-	0x86, /* 10000110 */
-	0x7c, /* 01111100 */
-	},{ 0x16,8,8,SF_BITMAP,/* 22 0x16 '^V' */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0x7e, /* 01111110 */
-	0x7e, /* 01111110 */
-	0x7e, /* 01111110 */
-	0x00, /* 00000000 */
-	},{ 0x17,8,8,SF_BITMAP,/* 23 0x17 '^W' */
-	0x18, /* 00011000 */
-	0x3c, /* 00111100 */
-	0x7e, /* 01111110 */
-	0x18, /* 00011000 */
-	0x7e, /* 01111110 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	0xff, /* 11111111 */
-	},{ 0x18,8,8,SF_BITMAP,/* 24 0x18 '^X' */
-	0x18, /* 00011000 */
-	0x3c, /* 00111100 */
-	0x7e, /* 01111110 */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x00, /* 00000000 */
-	},{ 0x19,8,8,SF_BITMAP,/* 25 0x19 '^Y' */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x18, /* 00011000 */
-	0x7e, /* 01111110 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	0x00, /* 00000000 */
-	},{ 0x1a,8,8,SF_BITMAP,/* 26 0x1a '^Z' */
-	0x00, /* 00000000 */
-	0x18, /* 00011000 */
-	0x0c, /* 00001100 */
-	0xfe, /* 11111110 */
-	0x0c, /* 00001100 */
-	0x18, /* 00011000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x1b,8,8,SF_BITMAP,/* 27 0x1b '^[' */
-	0x00, /* 00000000 */
-	0x30, /* 00110000 */
-	0x60, /* 01100000 */
-	0xfe, /* 11111110 */
-	0x60, /* 01100000 */
-	0x30, /* 00110000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x1c,8,8,SF_BITMAP,/* 28 0x1c '^\' */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0xc0, /* 11000000 */
-	0xc0, /* 11000000 */
-	0xc0, /* 11000000 */
-	0xfe, /* 11111110 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x1d,8,8,SF_BITMAP,/* 29 0x1d '^]' */
-	0x00, /* 00000000 */
-	0x24, /* 00100100 */
-	0x66, /* 01100110 */
-	0xff, /* 11111111 */
-	0x66, /* 01100110 */
-	0x24, /* 00100100 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x1e,8,8,SF_BITMAP,/* 30 0x1e '^^' */
-	0x00, /* 00000000 */
-	0x18, /* 00011000 */
-	0x3c, /* 00111100 */
-	0x7e, /* 01111110 */
-	0xff, /* 11111111 */
-	0xff, /* 11111111 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x1f,8,8,SF_BITMAP,/* 31 0x1f '^_' */
-	0x00, /* 00000000 */
-	0xff, /* 11111111 */
-	0xff, /* 11111111 */
-	0x7e, /* 01111110 */
-	0x3c, /* 00111100 */
-	0x18, /* 00011000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x7f,8,8,SF_BITMAP,/* 127 0x7f '' */
-	0x00, /* 00000000 */
-	0x10, /* 00010000 */
-	0x38, /* 00111000 */
-	0x6c, /* 01101100 */
-	0xc6, /* 11000110 */
-	0xc6, /* 11000110 */
-	0xfe, /* 11111110 */
-	0x00, /* 00000000 */
-	},{ 0x2591,8,8,SF_BITMAP,/* LIGHT SHADE */
-	0x22, /* 00100010 */
-	0x88, /* 10001000 */
-	0x22, /* 00100010 */
-	0x88, /* 10001000 */
-	0x22, /* 00100010 */
-	0x88, /* 10001000 */
-	0x22, /* 00100010 */
-	0x88, /* 10001000 */
-	},{ 0x2592,8,8,SF_BITMAP,/* MEDIUM SHADE */
-	0x55, /* 01010101 */
-	0xaa, /* 10101010 */
-	0x55, /* 01010101 */
-	0xaa, /* 10101010 */
-	0x55, /* 01010101 */
-	0xaa, /* 10101010 */
-	0x55, /* 01010101 */
-	0xaa, /* 10101010 */
-	},{ 0x2593,8,8,SF_BITMAP,/* DARK SHADE */
-	0x77, /* 01110111 */
-	0xdd, /* 11011101 */
-	0x77, /* 01110111 */
-	0xdd, /* 11011101 */
-	0x77, /* 01110111 */
-	0xdd, /* 11011101 */
-	0x77, /* 01110111 */
-	0xdd, /* 11011101 */
-	},{ 0x221a,8,8,SF_BITMAP,/* SQUARE ROOT */
-	0x78, /* 01111000 */
-	0x0c, /* 00001100 */
-	0x18, /* 00011000 */
-	0x30, /* 00110000 */
-	0x7c, /* 01111100 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	0x00, /* 00000000 */
-	},{ 0x2320,8,8,SF_BITMAP,/* UPPER INTERVAL*/
-        0x0e, /* 00001110 */
-        0x1b, /* 00011011 */
-        0x1b, /* 00011011 */
-        0x18, /* 00011000 */
-        0x18, /* 00011000 */
-        0x18, /* 00011000 */
-        0x18, /* 00011000 */
-        0x18, /* 00011000 */
-	},{ 0x25a0,8,8,SF_FILLRECT,/* BLACK SQUARE */
-		0x2244,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00111100 */
-		/* 00111100 */
-		/* 00111100 */
-		/* 00111100 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2502,8,8,SF_FILLRECT,/*BOX DRAWINGS LIGHT VERTICAL*/
-		0x3028,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2524,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
-		0x3028,
-		0x0431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2561,8,8,SF_FILLRECT,/*BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE*/
-		0x3028,
-		0x0231,
-		0x0431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2562,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE */
-		0x2028,
-		0x5028,
-		0x0421,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 11110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2556,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE */
-		0x0471,
-		0x2523,
-		0x5523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2555,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE */
-		0x3226,
-		0x0231,
-		0x0431,
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2563,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND LEFT*/
-		0x2022,
-		0x0221,
-		0x0421,
-		0x2424,
-		0x5028,
-		/* 00110110 */
-		/* 00110110 */
-		/* 11110110 */
-		/* 00000110 */
-		/* 11110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2551,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL */
-		0x2028,
-		0x5028,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2557,8,8,SF_FILLRECT,/*  BOX DRAWINGS DOUBLE DOWN AND LEFT */
-		0x0271,
-		0x5325,
-		0x0441,
-		0x2523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111110 */
-		/* 00000110 */
-		/* 11110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x255d,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND LEFT */
-		0x2022,
-		0x0241,
-		0x5025,
-		0x0451,
-		/* 00110110 */
-		/* 00110110 */
-		/* 11110110 */
-		/* 00000110 */
-		/* 11111110 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x255c,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND LEFT SINGLE */
-		0x2024,
-		0x5024,
-		0x0471,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 11111110 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x255b,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND LEFT DOUBLE */
-		0x3025,
-		0x0231,
-		0x0431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2510,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND LEFT */
-		0x0451,
-		0x3523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2514,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND RIGHT */
-		0x3025,
-		0x5431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2534,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
-		0x3024,
-		0x0481,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x252c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
-		0x0481,
-		0x3523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x251c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
-		0x3028,
-		0x5431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2500,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT HORIZONTAL */
-		0x0481,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x253c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
-		0x3028,
-		0x0481,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x255e,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE */
-		0x3028,
-		0x5231,
-		0x5431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x255f,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE */
-		0x2028,
-		0x5028,
-		0x7411,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x255a,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND RIGHT */
-		0x2025,
-		0x5023,
-		0x7211,
-		0x4441,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110111 */
-		/* 00110000 */
-		/* 00111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2554,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND RIGHT */
-		0x2261,
-		0x2325,
-		0x5424,
-		0x7411,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00111111 */
-		/* 00110000 */
-		/* 00110111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2569,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND HORIZONTAL */
-		0x2022,
-		0x0241,
-		0x5022,
-		0x5231,
-		0x0481,
-		/* 00110110 */
-		/* 00110110 */
-		/* 11110111 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2566,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL */
-		0x0281,
-		0x0441,
-		0x2523,
-		0x5431,
-		0x5523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 11110111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2560,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND RIGHT */
-		0x2028,
-		0x5022,
-		0x5231,
-		0x5431,
-		0x5623,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110111 */
-		/* 00110000 */
-		/* 00110111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2550,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE HORIZONTAL */
-		0x0281,
-		0x0481,
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x256c,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL */
-		0x2022,
-		0x0241,
-		0x5022,
-		0x5231,
-		0x0441,
-		0x2523,
-		0x5431,
-		0x5523,
-		/* 00110110 */
-		/* 00110110 */
-		/* 11110111 */
-		/* 00000000 */
-		/* 11110111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2567,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE */
-		0x3022,
-		0x0281,
-		0x0481,
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2568,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE */
-		0x2024,
-		0x5024,
-		0x0481,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2564,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE */
-		0x0281,
-		0x0481,
-		0x3523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2565,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE */
-		0x0481,
-		0x2523,
-		0x5523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x2559,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE */
-		0x2024,
-		0x5024,
-		0x2461,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2558,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE */
-		0x3025,
-		0x5231,
-		0x5431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x2552,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE */
-		0x3226,
-		0x5231,
-		0x5431,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2553,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE */
-		0x2461,
-		0x2523,
-		0x5523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00111111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x256b,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE */
-		0x2028,
-		0x5028,
-		0x0481,
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 11111111 */
-		/* 00110110 */
-		/* 00110110 */
-		/* 00110110 */
-	},{ 0x256a,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE */
-		0x3028,
-		0x0281,
-		0x0481,
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111111 */
-		/* 00011000 */
-		/* 11111111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2518,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND LEFT */
-		0x3025,
-		0x0431,
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 11111000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	},{ 0x250c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND RIGHT */
-		0x3451,
-		0x3523,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00011111 */
-		/* 00011000 */
-		/* 00011000 */
-		/* 00011000 */
-	},{ 0x2588,8,8,SF_FILLRECT,/* FULL BLOCK */
-		0x0088,
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-	},{ 0x2584,8,8,SF_FILLRECT,/* LOWER HALF BLOCK */
-		0x0484,
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-	},{ 0x258c,8,8,SF_FILLRECT,/* LEFT HALF BLOCK */
-		0x0048,
-		/* 11110000 */
-		/* 11110000 */
-		/* 11110000 */
-		/* 11110000 */
-		/* 11110000 */
-		/* 11110000 */
-		/* 11110000 */
-		/* 11110000 */
-	},{ 0x2590,8,8,SF_FILLRECT,/* RIGHT HALF BLOCK */
-		0x4048,
-		/* 00001111 */
-		/* 00001111 */
-		/* 00001111 */
-		/* 00001111 */
-		/* 00001111 */
-		/* 00001111 */
-		/* 00001111 */
-		/* 00001111 */
-	},{ 0x2580,8,8,SF_FILLRECT,/* UPPER HALF BLOCK */
-		0x0084,
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 11111111 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-		/* 00000000 */
-	}};
-
-	public SoftFont() {
-		font = new java.util.Hashtable();
-		for (int i=0;i<fontdata.length;i++)
-			font.put(new Integer(fontdata[i][0]),new Integer(i));
-
-	}
-
-	public boolean inSoftFont(char c) {
-		boolean insoftfont;
-
-		insoftfont = (null!=font.get(new Integer(c)));
-		if (!insoftfont && (int)c>=0x100) {
-			System.out.println("Character "+((int)c)+" not in softfont");
-		}
-		return insoftfont;
-	}
-
-	public void drawChar(Graphics g,char c,int x,int y,int cw,int ch) {
-		double	dw,dh;
-		Object	Ientry;
-		int	w,h,entry,i,fontwidth,fontheight;
-		
-		Ientry = font.get(new Integer(c));
-		if (Ientry == null)
-			return;
-		entry = ((Integer)Ientry).intValue();
-		fontwidth = fontdata[entry][SF_WIDTH];
-		fontheight = fontdata[entry][SF_HEIGHT];
-
-		dw = cw*1.0/fontwidth;
-		dh = ch*1.0/fontheight;
-
-		switch (fontdata[entry][SF_TYPE]) {
-		case SF_BITMAP:
-			for (h=0;h<fontheight;h++) {
-				for (w=0;w<fontwidth;w++) {
-					//FIXME: 8 bit max currently...
-					if (0!=(fontdata[entry][h+SF_DATA] & (1<<(7-w)))) {
-						g.fillRect(
-							x+(int)(w*dw),
-							y+(int)(h*dh),
-							((int)((w+1)*dw))-(int)(w*dw),
-							((int)((h+1)*dh))-(int)(h*dh)
-						);
-					}
-				}
-			}
-			break;
-		case SF_FILLRECT:
-			i=SF_DATA;
-			while (i<fontdata[entry].length) {
-				int	xw,xh;
-
-				w=(fontdata[entry][i]&0xF000)>>12;
-				h=(fontdata[entry][i]&0x0F00)>>8;
-				xw = (fontdata[entry][i]&0x00F0)>>4;
-				xh = (fontdata[entry][i]&0x000F);
-				g.fillRect(
-					x+(int)(w*dw),
-					y+(int)(h*dh),
-					((int)((w+xw)*dw))-(int)(w*dw),
-					((int)((h+xh)*dh))-(int)(h*dh)
-				);
-				i++;
-			}
-			break;
-		default:
-			break;
-		}
-	}
-}
diff --git a/web/root/telnet/display/Terminal.class b/web/root/telnet/display/Terminal.class
deleted file mode 100644
index 306194a18f27bca5ea14723f54b056893f459902..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 509
zcmX^0Z`VEsW(HjbId%q7b_Pi<1||j;9tLg(c18v<4Nae{#Ii*F#PSk-m(1MMyyDFK
zJZnY<5tu|yVqUs_a7j^SUb;0S1G9!^7+6<yG(;b&G>e8aNM2L}QxYW3W|NthSz^b?
z!061#z~Y>rlFG;+<dc$FT#%DksUMPBl$)8Cm}AYz!0(fpm+DuVo0M7<l9-eORt9z^
zs&zs>`N@enVTnbVAVpA>T*3LJMaijdnILVvFhlh~+8G%blNlKV&@E(S5J*og2}mqT
z%uOvxE%MAu%V%U@Pfslg&a6shWZ+LvErA&mQdyA7$iN3O3>;(uiFv6xj124rr6tZ8
ziA9VIoCT#NkkDdeU@T^2U@6JSEM`$;U}Ruo00A}zMo`EzFf#BmFfiydFfuSQFfg!c
zZD(NK$iTqB#K6zMzyMOjz@W+?$iTqB15v;r%fQFLz{1EN#=rrl#Ti(^v;+ein3iJT
S1k=(CTnr2hj0{3x(}V#P-g9aI

diff --git a/web/root/telnet/display/Terminal.java b/web/root/telnet/display/Terminal.java
deleted file mode 100644
index 30cb6d0a4e..0000000000
--- a/web/root/telnet/display/Terminal.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Terminal -- Terminal emulation (abstract class)
- * --
- * $Id: Terminal.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Wed Mar  5 11:27:13 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package display;
-
-import java.awt.Panel;
-import java.awt.Dimension;
-
-/**
- * Terminal is an abstract emulation class.
- * It contains a character display.
- *
- * @version $Id: Terminal.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- */
-public abstract class Terminal extends Panel
-{
-	/**
-	 * Get the specific parameter info for the emulation.
-	 * @see java.applet.Applet
-	 */
-	public abstract String[][] getParameterInfo();
-  
-	/**
-	 * Put a character on the screen. The method has to see if it is
-	 * a special character that needs to be handles special.
-	 * @param c the character
-	 * @see #putString
-	 */
-	public abstract void putChar(char c);
-	
-	/**
-	 * Put a character on the screen. The method has to parse the string
-	 * may handle special characters.
-	 * @param s the string
-	 * @see #putString
-	 */
-	public abstract void putString(String s);
-
-	/**
-	 * Return the current size of the terminal in characters.
-	 */
-	public abstract Dimension getSize();
-  
-	/**
-	 * Return actual terminal type identifier.
-	 */
-	public abstract String getTerminalType();
-}
diff --git a/web/root/telnet/display/TerminalHost.class b/web/root/telnet/display/TerminalHost.class
deleted file mode 100644
index 5fb34238c0b8d2e1d9ed6b08ea0b9bd0fc818542..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 255
zcmX^0Z`VEsW(Hjb0d@v%b_O0s22l;4ti-ZJ{hY+Sbp7CxqRhN>Yt1M|1{UZ1lvG9r
zF`tyo;)0ySO8t=3qTI~9#2k<O;u32{27aH+yi~u^+@#c^ki?{%R7M6NOoKpz`N@en
zVTnbVAVpA>T*3LJMaijdnILU~$Oh|y+``Bpg3VG!1_6*P#HId8S*gh-j0}v$j0`Nr
qsd*`k3@jxXnZ<0342%p+3?RVB!pOkKz|6qFz{tSKz`(%7zy$zRAVs<W

diff --git a/web/root/telnet/display/TerminalHost.java b/web/root/telnet/display/TerminalHost.java
deleted file mode 100644
index 29bdf6c0f3..0000000000
--- a/web/root/telnet/display/TerminalHost.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * TerminalHost -- this interface defines the remote end of the connection
- *                 from our Terminal to the Host (virtual).
- * --
- * $Id: TerminalHost.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Wed Mar  5 12:01:31 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package display;
-
-/**
- * TerminalHost is an interface for the remote (virtual) end of our connection
- * to the host computer we are connected to.
- * @version $Id: TerminalHost.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author Matthias L Jugel, Marcus Mei�ner
- */
-public interface TerminalHost
-{
-	/**
-	 * Send a string to the host and return if it was received successfully.
-	 * @param s the string to send
-	 * @return True for successful receivement.
-	 */
-	public boolean send(String s);
-}
-
diff --git a/web/root/telnet/display/vt320.class b/web/root/telnet/display/vt320.class
deleted file mode 100644
index ee8e4b86e851d2554b71d612cca520e7e8a38ab2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 30926
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
C<IITw

diff --git a/web/root/telnet/display/vt320.java b/web/root/telnet/display/vt320.java
deleted file mode 100644
index 81ebb13331..0000000000
--- a/web/root/telnet/display/vt320.java
+++ /dev/null
@@ -1,2021 +0,0 @@
-/*
- * vt320 -- a DEC VT320 Terminal emulation
- * --
- * $Id: vt320.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package display;
-
-import java.awt.Scrollbar;
-import java.awt.Event;
-import java.awt.Dimension;
-import java.awt.BorderLayout;
-import java.awt.FlowLayout;
-import java.util.Vector;
-
-import java.applet.Applet;
-
-/**
- * A DEC VT320 Terminal Emulation (includes VT100/220 and ANSI).
- *
- * The terminal emulation accesses the applet parameters to configure itself.
- * The following parameters may be set. Default values are written in
- * <I>italics</I> and other possible values are <B>bold</B>.
- * <DL>
- *  <DT><TT>&lt;PARAM NAME="Fx" VALUE="<I>functionkeytext</I>"&gt</TT>
- *  <DD>Sets the string sent when the function key Fx (x between 1 und 20)
- *	is pressed.
- *  <DT><TT>&lt;PARAM NAME="VTcolumns" VALUE="<I>80</I>"&gt</TT>
- *  <DD>Sets the columns of the terminal initially. If the parameter
- *      VTresize is set to <B>screen</B> this may change, else it is fixed.
- *  <DT><TT>&lt;PARAM NAME="VTrows" VALUE="<I>24</I>"&gt</TT>
- *  <DD>Sets the rows of the terminal initially. If the parameter
- *      value of VTresize <B>screen</B> this may change!
- *  <DT><TT>&lt;PARAM NAME="VTfont" VALUE="<I>Courier</I>"&gt</TT>
- *  <DD>Sets the font to be used for the terminal. It is recommended to
- *      use <I>Courier</I> or at least a fixed width font.
- *  <DT><TT>&lt;PARAM NAME="VTfontsize" VALUE="<I>14</I>"&gt</TT>
- *  <DD>Sets the font size for the terminal. If the parameter
- *      value of VTresize is set to <B>font</B> this may change!
- *  <DT><TT>&lt;PARAM NAME="VTresize" VALUE="<I>font</I>"&gt</TT>
- *  <DD>This parameter determines what the terminal should do if the window
- *      is resized. The default setting <I><B>font</B></I> will result in
- *      resizing the font until is matches the window best. Other possible
- *      values are <B>none</B> or <B>screen</B>. <B>none</B> will let nothing
- *      happen and <B>screen</B> will let the display try to change the
- *      amount of rows and columns to match the window best.
- *  <DT><TT>&lt;PARAM NAME="VTscrollbar" VALUE="<I>false</I>"&gt</TT>
- *  <DD>Setting this parameter to <B>true</B> will add a scrollbar west to
- *      the terminal. Other possible values include <B>left</B> to put the
- *      scrollbar on the left side of the terminal and <B>right</B> to put it
- *      explicitely to the right side.
- *  <DT><TT>&lt;PARAM NAME="VTid" VALUE="<I>vt320</I>"&gt</TT>
- *  <DD>This parameter will override the terminal id <I>vt320</I>. It may
- *      be used to determine special terminal abilities of VT Terminals.
- *  <DT><TT>&lt;PARAM NAME="VTbuffer" VALUE="<I>xx</I>"&gt</TT>
- *  <DD>Initially this parameter is the same as the VTrows parameter. It
- *      cannot be less than the amount of rows on the display. It determines
- *      the available scrollback buffer.
- *  <DT><TT>&lt;PARAM NAME="VTcharset" VALUE="<I>none</I>"&gt</TT>
- *  <DD>Setting this parameter to <B>ibm</B> will enable mapping of ibm
- *      characters (as used in PC BBS systems) to UNICODE characters. Note
- *      that those special characters probably won't show on UNIX systems
- *      due to lack in X11 UNICODE support.
- *  <DT><TT>&lt;PARAM NAME="VTvms" VALUE="<I>false</I>"&gt</TT>
- *  <DD>Setting this parameter to <B>true</B> will change the Backspace key
- *      into a delete key, cause the numeric keypad keys to emit VT100
- *      codes when Ctrl is pressed, and make other VMS-important keyboard
- *      definitions.
- *  <DT><TT>&lt;PARAM NAME="F<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
- *  <DD>Function keys from <I>F1</I> to <I>F20</I> are programmable. You can
- *      install any possible string including special characters, such as
- *      <TABLE BORDER>
- *      <TR><TD><TT>\e</TT></TD><TD>Escape</TD>
- *      <TR><TD><TT>\b</TT></TD><TD>Backspace</TD>
- *      <TR><TD><TT>\n</TT></TD><TD>Newline</TD>
- *      <TR><TD><TT>\r</TT></TD><TD>Return</TD>
- *      <TR><TD><TT>\xxxx</TT></TD><TD>Character xxxx (decimal)</TD>
- *      </TABLE>
- *  <DT><TT>&lt;PARAM NAME="CF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
- *  <DD>Function keys (with the Control-key pressed) from <I>CF1</I> to <I>CF20</I> are programmable too.
- *  <DT><TT>&lt;PARAM NAME="SF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
- *  <DD>Function keys (with the Shift-key pressed) from <I>SF1</I> to <I>SF20</I> are programmable too.
- *  <DT><TT>&lt;PARAM NAME="AF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
- *  <DD>Function keys (with the Alt-key pressed) from <I>AF1</I> to <I>AF20</I> are programmable too.
- * </DL>
- * @version $Id: vt320.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei?ner
- */
-public class vt320 extends Terminal implements TerminalHost
-{
-  /**
-   * Return the version of the terminal emulation and its display.
-   */
-  public String toString() { return "$Id: vt320.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $ "+display.version; }
-
-  // the input handler takes the keyboard input from us.
-  private TerminalHost host = this;
-
-  // due to a bug with Windows we need a keypress cache
-  private int pressedKey = ' ';
-  private long pressedWhen = ' ';
-
-  // The character display
-  private CharDisplay display;
-  private static int debug = 0;
-  private String terminalID = "vt320";
-
-  // X - COLUMNS, Y - ROWS
-  int  R,C;
-  int  Sc,Sr,Sa;
-  int  attributes  = 0;
-  int  insertmode  = 0;
-  int  statusmode = 0;
-  int  vt52mode  = 0;
-  int  normalcursor  = 0;
-  boolean moveoutsidemargins = true;
-  boolean sendcrlf = true;
-
-  private boolean  useibmcharset = false;
-
-  private  static  int  lastwaslf = 0;
-  private static  int i;
-  private final static char ESC = 27;
-  private final static char IND = 132;
-  private final static char NEL = 133;
-  private final static char RI  = 141;
-  private final static char HTS = 136;
-  private final static char DCS = 144;
-  private final static char CSI = 155;
-  private final static char OSC = 157;
-  private final static int TSTATE_DATA  = 0;
-  private final static int TSTATE_ESC  = 1; /* ESC */
-  private final static int TSTATE_CSI  = 2; /* ESC [ */
-  private final static int TSTATE_DCS  = 3; /* ESC P */
-  private final static int TSTATE_DCEQ  = 4; /* ESC [? */
-  private final static int TSTATE_ESCSQUARE= 5; /* ESC # */
-  private final static int TSTATE_OSC= 6;       /* ESC ] */
-  private final static int TSTATE_SETG0= 7;     /* ESC (? */
-  private final static int TSTATE_SETG1= 8;     /* ESC )? */
-  private final static int TSTATE_SETG2= 9;     /* ESC *? */
-  private final static int TSTATE_SETG3= 10;    /* ESC +? */
-  private final static int TSTATE_CSI_DOLLAR  = 11; /* ESC [ Pn $ */
-
-  /* The graphics charsets
-   * B - default ASCII
-   * A - default UK
-   * 0 - DEC SPECIAL
-   * < - User defined
-   * ....
-   */
-  private static char gx[] = {
-  'B',      // g0
-  '0',      // g1
-  'A',      // g2
-  '<',      // g3
-  };
-  private static char gr = 1;  // default GR to G1
-  private static char gl = 0;  // default GL to G0
-
-  // array to store DEC Special -> Unicode mapping
-  //  Unicode   DEC  Unicode name    (DEC name)
-  private static char DECSPECIAL[] = {
-    '\u0040', //5f blank
-    '\u2666', //60 black diamond
-    '\u2592', //61 grey square
-    '\u2409', //62 Horizontal tab  (ht) pict. for control
-    '\u240c', //63 Form Feed       (ff) pict. for control
-    '\u240d', //64 Carriage Return (cr) pict. for control
-    '\u240a', //65 Line Feed       (lf) pict. for control
-    '\u00ba', //66 Masculine ordinal indicator
-    '\u00b1', //67 Plus or minus sign
-    '\u2424', //68 New Line        (nl) pict. for control
-    '\u240b', //69 Vertical Tab    (vt) pict. for control
-    '\u2518', //6a Forms light up   and left
-    '\u2510', //6b Forms light down and left
-    '\u250c', //6c Forms light down and right
-    '\u2514', //6d Forms light up   and right
-    '\u253c', //6e Forms light vertical and horizontal
-    '\u2594', //6f Upper 1/8 block                        (Scan 1)
-    '\u2580', //70 Upper 1/2 block                        (Scan 3)
-    '\u2500', //71 Forms light horizontal or ?em dash?    (Scan 5)
-    '\u25ac', //72 \u25ac black rect. or \u2582 lower 1/4 (Scan 7)
-    '\u005f', //73 \u005f underscore  or \u2581 lower 1/8 (Scan 9)
-    '\u251c', //74 Forms light vertical and right
-    '\u2524', //75 Forms light vertical and left
-    '\u2534', //76 Forms light up   and horizontal
-    '\u252c', //77 Forms light down and horizontal
-    '\u2502', //78 vertical bar
-    '\u2264', //79 less than or equal
-    '\u2265', //7a greater than or equal
-    '\u00b6', //7b paragraph
-    '\u2260', //7c not equal
-    '\u00a3', //7d Pound Sign (british)
-    '\u00b7'  //7e Middle Dot
-  };
-
-  private final static int KEYUP  = Event.UP % 1000;
-  private final static int KEYDOWN  = Event.DOWN % 1000;
-  private final static int KEYLEFT  = Event.LEFT % 1000;
-  private final static int KEYRIGHT  = Event.RIGHT % 1000;
-  private final static int KEYF1  = Event.F1 % 1000;
-  private final static int KEYF2  = Event.F2 % 1000;
-  private final static int KEYF3  = Event.F3 % 1000;
-  private final static int KEYF4  = Event.F4 % 1000;
-  private final static int KEYF5  = Event.F5 % 1000;
-  private final static int KEYF6  = Event.F6 % 1000;
-  private final static int KEYF7  = Event.F7 % 1000;
-  private final static int KEYF8  = Event.F8 % 1000;
-  private final static int KEYF9  = Event.F9 % 1000;
-  private final static int KEYF10  = Event.F10 % 1000;
-  private final static int KEYF11  = Event.F11 % 1000;
-  private final static int KEYF12  = Event.F12 % 1000;
-  private final static int KEYPGDN   = Event.PGDN % 1000;
-  private final static int KEYPGUP   = Event.PGUP % 1000;
-
-  private final static int KEYHOME  = Event.HOME % 1000;
-  private final static int KEYEND  = Event.END % 1000;
-
-  public static final int KEYPRINT_SCREEN  = 20;
-  public static final int KEYSCROLL_LOCK    = 21;
-  public static final int KEYCAPS_LOCK    = 22;
-  public static final int KEYNUM_LOCK    = 23;
-  public static final int KEYPAUSE    = 24;
-  public static final int KEYINSERT    = 25;
-
-    /**
-     * The Insert key.
-     */
-    public static final int INSERT    = 1025;
-
-  /**
-   * Strings to send on function key presseic
-   */
-  private String FunctionKey[];
-  private String FunctionKeyShift[];
-  private String FunctionKeyCtrl[];
-  private String FunctionKeyAlt[];
-  private String KeyUp;
-  private String KeyDown;
-  private String KeyLeft;
-  private String KeyRight;
-  private String KeyBacktab;
-  private String KeyTab;
-
-  private String KP0;
-  private String KP1;
-  private String KP2;
-  private String KP3;
-  private String KP4;
-  private String KP5;
-  private String KP6;
-  private String KP7;
-  private String KP8;
-  private String KP9;
-  private String KPMinus;
-  private String KPComma;
-  private String KPPeriod;
-  private String KPEnter;
-  private String PF1;
-  private String PF2;
-  private String PF3;
-  private String PF4;
-  private String Help;
-  private String Do;
-  private String Find;
-  private String Insert;
-  private String Remove;
-  private String Select;
-  private String PrevScn;
-  private String NextScn;
-
-
-  private String osc,dcs;  /* to memorize OSC & DCS control sequence */
-
-  private int term_state = TSTATE_DATA;
-  private boolean vms = false;
-  private byte[]  Tabs;
-  private int[]  DCEvars = new int [10];
-  private  int  DCEvar;
-
-  /* operation system we run on, Scrollbar hack */
-  private String osn = System.getProperty("os.name");
-
-  public String[][] getParameterInfo() {
-    String pinfo[][] = {
-    {"VTcolumns",  "Integer",   "Columns of the terminal"},
-    {"VTrows",     "Integer",   "Rows of the terminal"},
-    {"VTfont",     "String",    "Terminal font (default is Courier)"},
-    {"VTfontsize", "Integer",   "Font size"},
-    {"VTbuffer",   "Integer",   "Scrollback buffer size"},
-    {"VTscrollbar","Boolean",   "Enable or disable scrollbar"},
-    {"VTresize",   "String",    "One of none, font, screen"},
-    {"VTid",       "String",    "Terminal id, standard is VT320"},
-    {"VTcharset",  "String",    "Charset used"},
-    {"VTvms",      "Boolean",   "Enable or disable VMS key mappings"},
-    {"F1 - F20",   "String",    "Programmable Function Keys"},
-    {"SF1 - SF20",   "String",    "Programmable Shift-ed Function Keys"},
-    {"CF1 - CF20",   "String",    "Programmable Control-ed Function Keys"},
-    {"AF1 - AF20",   "String",    "Programmable Alt-ed Function Keys"},
-    };
-    return pinfo;
-  }
-
-  static String unEscape(String tmp) {
-      int idx = 0, oldidx = 0;
-      String cmd;
-
-      cmd = "";
-      while((idx = tmp.indexOf('\\', oldidx)) >= 0 &&
-            ++idx <= tmp.length()) {
-        cmd += tmp.substring(oldidx, idx-1);
-        if(idx == tmp.length()) return cmd;
-        switch(tmp.charAt(idx)) {
-        case 'b': cmd += "\b"; break;
-        case 'e': cmd += "\u001b"; break;
-        case 'n': cmd += "\n"; break;
-        case 'r': cmd += "\r"; break;
-        case 't': cmd += "\t"; break;
-        case 'v': cmd += "\u000b"; break;
-        case 'a': cmd += "\u0012"; break;
-        default : 
-	  if ( (tmp.charAt(idx)>='0') && (tmp.charAt(idx)<='9')) {
-	    for (i = idx;i<tmp.length();i++) {
-	      if ( (tmp.charAt(i)<'0') || (tmp.charAt(i)>'9'))
-	        break;
-	    }
-	    cmd += (char)(new java.lang.Integer(tmp.substring(idx, i)).intValue());
-	    idx = i-1;
-	  } else
-	    cmd += tmp.substring(idx, ++idx);break;
-	}
-	oldidx = ++idx;
-      }
-      if(oldidx <= tmp.length()) cmd += tmp.substring(oldidx);
-      return cmd;
-  }
-
-  /**
-   * Initialize terminal.
-   * @param parent the applet parent where to get parameters from
-   * @see display.Terminal
-   */
-  public void addNotify() {
-    if(display == null) {
-      String width = "80", height = "24", resize ="font";
-      String font = "Courier", fs = "14", bufs = "100";
-      String scrb = "false";
-      String vms = "false";
-      String ibmcset = "false";
-
-      Applet applet = (Applet)getParent();
-
-      if(applet != null) {
-        try {
-          host = (TerminalHost)applet;
-        } catch(ClassCastException e) {
-          System.err.println("vt320: "+applet+" cannot receive terminal input");
-          host = this;
-        }
-
-        width  = applet.getParameter("VTcolumns");
-        height = applet.getParameter("VTrows");
-        font   = applet.getParameter("VTfont");
-        fs     = applet.getParameter("VTfontsize");
-        bufs   = applet.getParameter("VTbuffer");
-        scrb   = applet.getParameter("VTscrollbar");
-        vms    = applet.getParameter("VTvms");
-        resize = applet.getParameter("VTresize");
-        resize = resize == null ? "font" : resize;
-        ibmcset = applet.getParameter("VTcharset");
-        if ((ibmcset!=null)&&(ibmcset.equals("ibm")))
-          useibmcharset=true;
-
-        if(applet.getParameter("VTid") != null)
-          terminalID = applet.getParameter("VTid");
-      }
-
-      display = new CharDisplay(
-          width==null?80:(new Integer(width)).intValue(),
-          (height==null?24:(new Integer(height)).intValue()),
-          font==null?"Courier":font,
-          fs==null?14:(new Integer(fs)).intValue()
-      );
-      display.setBottomMargin((height==null?
-			       24:
-			       (new Integer(height)).intValue()) - 1);
-      display.setBufferSize(bufs==null?100:(new Integer(bufs)).intValue());
-      if(resize.equals("none"))
-        display.setResizeStrategy(CharDisplay.RESIZE_NONE);
-      if(resize.equals("font"))
-        display.setResizeStrategy(CharDisplay.RESIZE_FONT);
-      if(resize.equals("screen"))
-        display.setResizeStrategy(CharDisplay.RESIZE_SCREEN);
-
-      display.setBorder(2, false);
-
-      setLayout(new BorderLayout());
-      if(scrb != null && !scrb.equals("false")) {
-        if(scrb.equals("left") || scrb.equals("true"))
-          display.setScrollbar("West");
-        else if(scrb.equals("right"))
-          display.setScrollbar("East");
-        else
-          System.out.println("vt320: unknown scrollbar location: "+scrb);
-      }
-      if(vms != null && vms.equals("true"))
-      {
-        this.vms = true;
-      }
-      add("Center", display);
-      InitTerminalVars();
-      for (int i=1;i<20;i++)
-      {
-      	if (applet.getParameter("F"+i)!=null)
-	  FunctionKey[i] = unEscape(applet.getParameter("F"+i));
-        if (applet.getParameter("SF"+i)!=null)
-          FunctionKeyShift[i] = unEscape(applet.getParameter("SF"+i));
-        if (applet.getParameter("CF"+i)!=null)
-          FunctionKeyCtrl[i] = unEscape(applet.getParameter("CF"+i));
-        if (applet.getParameter("AF"+i)!=null)
-          FunctionKeyAlt[i] = unEscape(applet.getParameter("AF"+i));
-      }
-    }
-    super.addNotify();
-  }
-
-  private void InitTerminalVars() {
-    int nw = display.getColumns();
-
-    if (nw<132) nw=132; //catch possible later 132/80 resizes
-    Tabs = new byte[nw];
-    for (int i=0;i<nw;i+=8) {
-      Tabs[i]=1;
-    }
-
-    PF1  = "\u001bOP";
-    PF2  = "\u001bOQ";
-    PF3  = "\u001bOR";
-    PF4  = "\u001bOS";
-
-    Find = "\u001b[1~";
-    Insert = "\u001b[2~";
-    Remove = "\u001b[3~";
-    Select = "\u001b[4~";
-    PrevScn = "\u001b[5~";
-    NextScn = "\u001b[6~";
-
-    Help = "\u001b[28~";
-    Do = "\u001b[29~";
-
-    FunctionKey = new String[21];
-    FunctionKey[0]= "";
-    FunctionKey[1]= PF1;
-    FunctionKey[2]= PF2;
-    FunctionKey[3]= PF3;
-    FunctionKey[4]= PF4;
-    /* following are defined differently for vt220 / vt132 ... */
-    FunctionKey[5]= "\u001b[15~";
-    FunctionKey[6]= "\u001b[17~";
-    FunctionKey[7]= "\u001b[18~";
-    FunctionKey[8]= "\u001b[19~";
-    FunctionKey[9]= "\u001b[20~";
-    FunctionKey[10]= "\u001b[21~";
-    FunctionKey[11]= "\u001b[23~";
-    FunctionKey[12]= "\u001b[24~";
-    FunctionKey[13]= "\u001b[25~";
-    FunctionKey[14]= "\u001b[26~";
-    FunctionKey[15]= Help;
-    FunctionKey[16]= Do;
-    FunctionKey[17]= "\u001b[31~";
-    FunctionKey[18]= "\u001b[32~";
-    FunctionKey[19]= "\u001b[33~";
-    FunctionKey[20]= "\u001b[34~";
-
-    FunctionKeyShift = new String[21];
-    FunctionKeyAlt = new String[21];
-    FunctionKeyCtrl = new String[21];
-
-    for (int i=0;i<20;i++)
-    {
-       FunctionKeyShift[i]="";
-       FunctionKeyAlt[i]="";
-       FunctionKeyCtrl[i]="";
-    }
-    FunctionKeyShift[15] = Find;
-    FunctionKeyShift[16] = Select;
-
-    KeyTab   = "\u0009";
-    KeyBacktab = "\u001bOP\u0009";
-    KeyUp    = "\u001b[A";
-    KeyDown  = "\u001b[B";
-    KeyRight  = "\u001b[C";
-    KeyLeft  = "\u001b[D";
-    KP0  = "\u001bOp";
-    KP1  = "\u001bOq";
-    KP2  = "\u001bOr";
-    KP3  = "\u001bOs";
-    KP4  = "\u001bOt";
-    KP5  = "\u001bOu";
-    KP6  = "\u001bOv";
-    KP7  = "\u001bOw";
-    KP8  = "\u001bOx";
-    KP9  = "\u001bOy";
-    KPMinus  = "\u001bOm";
-    KPComma  = "\u001bOl";
-    KPPeriod  = "\u001bOn";
-    KPEnter  = "\u001bOM";
-
-    /* ... */
-  }
-
-  public Dimension getSize() {
-    return new Dimension(display.getColumns(), display.getRows());
-  }
-
-  public String getTerminalType() { return terminalID; }
-
-  /**
-   * Handle events for the terminal. Only accept events for the scroll
-   * bar. Any other events have to be propagated to the parent.
-   * @param evt the event
-   */
-  public boolean handleEvent(Event evt) {
-    // request focus if mouse enters (necessary to avoid mistakes)
-    if(evt.id == Event.MOUSE_ENTER) { display.requestFocus(); return true; }
-    // give focus away if mouse leaves the window
-    if(evt.id == Event.MOUSE_EXIT) { nextFocus(); return true; }
-
-    // handle keyboard events
-
-    /* Netscape for windows does not send keyDown when period
-    * is pressed. This hack catches the keyUp event.
-    */
-    int id = evt.id;
-
-    boolean control = (((evt.CTRL_MASK & evt.modifiers)==0) ? false : true);
-    boolean shift = (((evt.SHIFT_MASK & evt.modifiers)==0) ? false : true );
-    boolean alt = (((evt.ALT_MASK & evt.modifiers)==0) ? false :true);
-
-    pressedKey:
-    {
-        if (pressedKey == 10
-            && (evt.key == 10
-            || evt.key == 13)
-            && evt.when - pressedWhen < 50)    //  Ray: Elliminate stuttering enter/return
-            break pressedKey;
-
-        int priorKey = pressedKey;
-        if(id == Event.KEY_PRESS)
-            pressedKey = evt.key;               //  Keep track of last pressed key
-        else if (evt.key == '.'
-            && evt.id != Event.KEY_RELEASE
-            && evt.key != pressedKey
-        ) {
-            pressedKey = 0;
-        } else
-            break pressedKey;
-        pressedWhen = evt.when;
-        if (evt.key == 10 && !control) {
-            if (sendcrlf)
-              host.send("\r\n"); /* YES, see RFC 854 */
-            else
-              host.send("\r"); /* YES, see RFC 854 */
-            return true;
-        } else
-/* FIXME: on german PC keyboards you have to use Alt-Ctrl-q to get an @,
- * so we can't just use it here... will probably break some other VMS stuff
- * -Marcus
-        if (((!vms && evt.key == '2') || evt.key == '@' || evt.key == ' ') && control)
- */
-        if (((!vms && evt.key == '2') || evt.key == ' ') && control)
-            return host.send("" + (char)0);
-        else if (vms) {
-	    if (evt.key == 8) {
-		if (shift && !control)
-		    return host.send("" + (char)10); //  VMS shift deletes word back
-		if (control && !shift)
-		    return host.send("" + (char)24); //  VMS control deletes line back
-		return host.send("" + (char)127);   //  VMS other is delete
-	    }
-	    if (evt.key == 127 && !control) {
-		if (shift)
-		    return host.send(Insert); //  VMS shift delete = insert
-		else
-		    return host.send(Remove); //  VMS delete = remove
-	    }
-            if (control) {
-                switch(evt.key) {
-                    case '0':
-                        return host.send(KP0);
-                    case '1':
-                        return host.send(KP1);
-                    case '2':
-                        return host.send(KP2);
-                    case '3':
-                        return host.send(KP3);
-                    case '4':
-                        return host.send(KP4);
-                    case '5':
-                        return host.send(KP5);
-                    case '6':
-                        return host.send(KP6);
-                    case '7':
-                        return host.send(KP7);
-                    case '8':
-                        return host.send(KP8);
-                    case '9':
-                        return host.send(KP9);
-                    case '.':
-                        return host.send(KPPeriod);
-                    case '-':
-                    case 31:
-                        return host.send(KPMinus);
-                    case '+':
-                        return host.send(KPComma);
-                    case 10:
-                        return host.send(KPEnter);
-                    case '/':
-                        return host.send(PF2);
-                    case '*':
-                        return host.send(PF3);
-                }
-                if (shift && evt.key < 32)
-                    return host.send(PF1+(char)(evt.key + 64));
-            }
-	}
-	// Hmmm. Outside the VMS case?
-        if (shift && (evt.key == '\t'))
-	    return host.send(KeyBacktab);
-	else
-	    return host.send(""+(char)evt.key);
-    }
-
-    String input = "";
-
-    if (evt.id == evt.KEY_ACTION)
-    {
-      String fmap[];
-      /* bloodsucking little buggers at netscape
-       * don't keep to the standard java values
-       * of <ARROW> or <Fx>
-       */
-      fmap = FunctionKey;
-      if (shift)
-	fmap = FunctionKeyShift;
-      if (control)
-        fmap = FunctionKeyCtrl;
-      if (alt)
-        fmap = FunctionKeyAlt;
-      switch (evt.key % 1000) {
-      case KEYF1:
-        input = fmap[1];
-        break;
-      case KEYF2:
-        input = fmap[2];
-        break;
-      case KEYF3:
-        input = fmap[3];
-        break;
-      case KEYF4:
-        input = fmap[4];
-        break;
-      case KEYF5:
-        input = fmap[5];
-        break;
-      case KEYF6:
-        input = fmap[6];
-        break;
-      case KEYF7:
-        input = fmap[7];
-        break;
-      case KEYF8:
-        input = fmap[8];
-        break;
-      case KEYF9:
-        input = fmap[9];
-        break;
-      case KEYF10:
-        input = fmap[10];
-        break;
-      case KEYF11:
-        input = fmap[11];
-        break;
-      case KEYF12:
-        input = fmap[12];
-        break;
-      case KEYUP:
-        input = KeyUp;
-        break;
-      case KEYDOWN:
-        input = KeyDown;
-        break;
-      case KEYLEFT:
-        input = KeyLeft;
-        break;
-      case KEYRIGHT:
-        input = KeyRight;
-        break;
-      case KEYPGDN:
-        input = NextScn;
-        break;
-      case KEYPGUP:
-        input = PrevScn;
-        break;
-      case KEYINSERT:
-        input = Insert;
-        break;
-//  The following cases fall through if not in VMS mode.
-      case KEYHOME:
-        if (vms)
-        {
-            input = "" + (char)8;
-            break;
-        }
-	//FALLTHROUGH
-      case KEYEND:
-        if (vms)
-        {
-            input = "" + (char)5;
-            break;
-        }
-	//FALLTHROUGH
-      case KEYNUM_LOCK:
-        if (vms && control) {
-            if (pressedKey != evt.key) {
-                pressedKey = evt.key;
-                input = PF1;
-            } else
-                pressedKey = ' ';   //  Here, we eat a second numlock since that returns numlock state
-        }
-	break;
-      default:
-        /*if (debug>0)*/
-          System.out.println("vt320: unknown event:"+(int)evt.key);
-        break;
-      }
-    }
-
-    if(input != null && input.length() > 0)
-      return host.send(input);
-
-    return false;
-  }
-
-  /**
-   * Dummy method to handle input events (String).
-   * This is only needed if our parent is not TerminalHost
-   * @param s String to handle
-   * @return always true
-   * @see display.TerminalHost
-   */
-  public boolean send(String s) {
-    putString(s);
-    return true;
-  }
-
-  private void handle_dcs(String dcs) {
-    System.out.println("DCS: "+dcs);
-  }
-  private void handle_osc(String osc) {
-    System.out.println("OSC: "+osc);
-  }
-
-  /**
-  * Put String at current cursor position. Moves cursor
-  * according to the String. Does NOT wrap.
-  * @param s the string
-  */
-  public void putString(String s) {
-    int  i,len=s.length();
-
-    display.markLine(R,1);
-    for (i=0;i<len;i++)
-      putChar(s.charAt(i),false);
-    display.setCursorPos(C, R);
-    display.redraw();
-  }
-
-  public void putChar(char c) {
-    putChar(c,true);
-    display.redraw();
-  }
-
-  public final static char unimap[] = {
-//#
-//#    Name:     cp437_DOSLatinUS to Unicode table
-//#    Unicode version: 1.1
-//#    Table version: 1.1
-//#    Table format:  Format A
-//#    Date:          03/31/95
-//#    Authors:       Michel Suignard <michelsu@microsoft.com>
-//#                   Lori Hoerth <lorih@microsoft.com>
-//#    General notes: none
-//#
-//#    Format: Three tab-separated columns
-//#        Column #1 is the cp1255_WinHebrew code (in hex)
-//#        Column #2 is the Unicode (in hex as 0xXXXX)
-//#        Column #3 is the Unicode name (follows a comment sign, '#')
-//#
-//#    The entries are in cp437_DOSLatinUS order
-//#
-
-  0x0000,// #NULL
-  0x0001,// #START OF HEADING
-  0x0002,// #START OF TEXT
-  0x0003,// #END OF TEXT
-  0x0004,// #END OF TRANSMISSION
-  0x0005,// #ENQUIRY
-  0x0006,// #ACKNOWLEDGE
-  0x0007,// #BELL
-  0x0008,// #BACKSPACE
-  0x0009,// #HORIZONTAL TABULATION
-  0x000a,// #LINE FEED
-  0x000b,// #VERTICAL TABULATION
-  0x000c,// #FORM FEED
-  0x000d,// #CARRIAGE RETURN
-  0x000e,// #SHIFT OUT
-  0x000f,// #SHIFT IN
-  0x0010,// #DATA LINK ESCAPE
-  0x0011,// #DEVICE CONTROL ONE
-  0x0012,// #DEVICE CONTROL TWO
-  0x0013,// #DEVICE CONTROL THREE
-  0x0014,// #DEVICE CONTROL FOUR
-  0x0015,// #NEGATIVE ACKNOWLEDGE
-  0x0016,// #SYNCHRONOUS IDLE
-  0x0017,// #END OF TRANSMISSION BLOCK
-  0x0018,// #CANCEL
-  0x0019,// #END OF MEDIUM
-  0x001a,// #SUBSTITUTE
-  0x001b,// #ESCAPE
-  0x001c,// #FILE SEPARATOR
-  0x001d,// #GROUP SEPARATOR
-  0x001e,// #RECORD SEPARATOR
-  0x001f,// #UNIT SEPARATOR
-  0x0020,// #SPACE
-  0x0021,// #EXCLAMATION MARK
-  0x0022,// #QUOTATION MARK
-  0x0023,// #NUMBER SIGN
-  0x0024,// #DOLLAR SIGN
-  0x0025,// #PERCENT SIGN
-  0x0026,// #AMPERSAND
-  0x0027,// #APOSTROPHE
-  0x0028,// #LEFT PARENTHESIS
-  0x0029,// #RIGHT PARENTHESIS
-  0x002a,// #ASTERISK
-  0x002b,// #PLUS SIGN
-  0x002c,// #COMMA
-  0x002d,// #HYPHEN-MINUS
-  0x002e,// #FULL STOP
-  0x002f,// #SOLIDUS
-  0x0030,// #DIGIT ZERO
-  0x0031,// #DIGIT ONE
-  0x0032,// #DIGIT TWO
-  0x0033,// #DIGIT THREE
-  0x0034,// #DIGIT FOUR
-  0x0035,// #DIGIT FIVE
-  0x0036,// #DIGIT SIX
-  0x0037,// #DIGIT SEVEN
-  0x0038,// #DIGIT EIGHT
-  0x0039,// #DIGIT NINE
-  0x003a,// #COLON
-  0x003b,// #SEMICOLON
-  0x003c,// #LESS-THAN SIGN
-  0x003d,// #EQUALS SIGN
-  0x003e,// #GREATER-THAN SIGN
-  0x003f,// #QUESTION MARK
-  0x0040,// #COMMERCIAL AT
-  0x0041,// #LATIN CAPITAL LETTER A
-  0x0042,// #LATIN CAPITAL LETTER B
-  0x0043,// #LATIN CAPITAL LETTER C
-  0x0044,// #LATIN CAPITAL LETTER D
-  0x0045,// #LATIN CAPITAL LETTER E
-  0x0046,// #LATIN CAPITAL LETTER F
-  0x0047,// #LATIN CAPITAL LETTER G
-  0x0048,// #LATIN CAPITAL LETTER H
-  0x0049,// #LATIN CAPITAL LETTER I
-  0x004a,// #LATIN CAPITAL LETTER J
-  0x004b,// #LATIN CAPITAL LETTER K
-  0x004c,// #LATIN CAPITAL LETTER L
-  0x004d,// #LATIN CAPITAL LETTER M
-  0x004e,// #LATIN CAPITAL LETTER N
-  0x004f,// #LATIN CAPITAL LETTER O
-  0x0050,// #LATIN CAPITAL LETTER P
-  0x0051,// #LATIN CAPITAL LETTER Q
-  0x0052,// #LATIN CAPITAL LETTER R
-  0x0053,// #LATIN CAPITAL LETTER S
-  0x0054,// #LATIN CAPITAL LETTER T
-  0x0055,// #LATIN CAPITAL LETTER U
-  0x0056,// #LATIN CAPITAL LETTER V
-  0x0057,// #LATIN CAPITAL LETTER W
-  0x0058,// #LATIN CAPITAL LETTER X
-  0x0059,// #LATIN CAPITAL LETTER Y
-  0x005a,// #LATIN CAPITAL LETTER Z
-  0x005b,// #LEFT SQUARE BRACKET
-  0x005c,// #REVERSE SOLIDUS
-  0x005d,// #RIGHT SQUARE BRACKET
-  0x005e,// #CIRCUMFLEX ACCENT
-  0x005f,// #LOW LINE
-  0x0060,// #GRAVE ACCENT
-  0x0061,// #LATIN SMALL LETTER A
-  0x0062,// #LATIN SMALL LETTER B
-  0x0063,// #LATIN SMALL LETTER C
-  0x0064,// #LATIN SMALL LETTER D
-  0x0065,// #LATIN SMALL LETTER E
-  0x0066,// #LATIN SMALL LETTER F
-  0x0067,// #LATIN SMALL LETTER G
-  0x0068,// #LATIN SMALL LETTER H
-  0x0069,// #LATIN SMALL LETTER I
-  0x006a,// #LATIN SMALL LETTER J
-  0x006b,// #LATIN SMALL LETTER K
-  0x006c,// #LATIN SMALL LETTER L
-  0x006d,// #LATIN SMALL LETTER M
-  0x006e,// #LATIN SMALL LETTER N
-  0x006f,// #LATIN SMALL LETTER O
-  0x0070,// #LATIN SMALL LETTER P
-  0x0071,// #LATIN SMALL LETTER Q
-  0x0072,// #LATIN SMALL LETTER R
-  0x0073,// #LATIN SMALL LETTER S
-  0x0074,// #LATIN SMALL LETTER T
-  0x0075,// #LATIN SMALL LETTER U
-  0x0076,// #LATIN SMALL LETTER V
-  0x0077,// #LATIN SMALL LETTER W
-  0x0078,// #LATIN SMALL LETTER X
-  0x0079,// #LATIN SMALL LETTER Y
-  0x007a,// #LATIN SMALL LETTER Z
-  0x007b,// #LEFT CURLY BRACKET
-  0x007c,// #VERTICAL LINE
-  0x007d,// #RIGHT CURLY BRACKET
-  0x007e,// #TILDE
-  0x007f,// #DELETE
-  0x00c7,// #LATIN CAPITAL LETTER C WITH CEDILLA
-  0x00fc,// #LATIN SMALL LETTER U WITH DIAERESIS
-  0x00e9,// #LATIN SMALL LETTER E WITH ACUTE
-  0x00e2,// #LATIN SMALL LETTER A WITH CIRCUMFLEX
-  0x00e4,// #LATIN SMALL LETTER A WITH DIAERESIS
-  0x00e0,// #LATIN SMALL LETTER A WITH GRAVE
-  0x00e5,// #LATIN SMALL LETTER A WITH RING ABOVE
-  0x00e7,// #LATIN SMALL LETTER C WITH CEDILLA
-  0x00ea,// #LATIN SMALL LETTER E WITH CIRCUMFLEX
-  0x00eb,// #LATIN SMALL LETTER E WITH DIAERESIS
-  0x00e8,// #LATIN SMALL LETTER E WITH GRAVE
-  0x00ef,// #LATIN SMALL LETTER I WITH DIAERESIS
-  0x00ee,// #LATIN SMALL LETTER I WITH CIRCUMFLEX
-  0x00ec,// #LATIN SMALL LETTER I WITH GRAVE
-  0x00c4,// #LATIN CAPITAL LETTER A WITH DIAERESIS
-  0x00c5,// #LATIN CAPITAL LETTER A WITH RING ABOVE
-  0x00c9,// #LATIN CAPITAL LETTER E WITH ACUTE
-  0x00e6,// #LATIN SMALL LIGATURE AE
-  0x00c6,// #LATIN CAPITAL LIGATURE AE
-  0x00f4,// #LATIN SMALL LETTER O WITH CIRCUMFLEX
-  0x00f6,// #LATIN SMALL LETTER O WITH DIAERESIS
-  0x00f2,// #LATIN SMALL LETTER O WITH GRAVE
-  0x00fb,// #LATIN SMALL LETTER U WITH CIRCUMFLEX
-  0x00f9,// #LATIN SMALL LETTER U WITH GRAVE
-  0x00ff,// #LATIN SMALL LETTER Y WITH DIAERESIS
-  0x00d6,// #LATIN CAPITAL LETTER O WITH DIAERESIS
-  0x00dc,// #LATIN CAPITAL LETTER U WITH DIAERESIS
-  0x00a2,// #CENT SIGN
-  0x00a3,// #POUND SIGN
-  0x00a5,// #YEN SIGN
-  0x20a7,// #PESETA SIGN
-  0x0192,// #LATIN SMALL LETTER F WITH HOOK
-  0x00e1,// #LATIN SMALL LETTER A WITH ACUTE
-  0x00ed,// #LATIN SMALL LETTER I WITH ACUTE
-  0x00f3,// #LATIN SMALL LETTER O WITH ACUTE
-  0x00fa,// #LATIN SMALL LETTER U WITH ACUTE
-  0x00f1,// #LATIN SMALL LETTER N WITH TILDE
-  0x00d1,// #LATIN CAPITAL LETTER N WITH TILDE
-  0x00aa,// #FEMININE ORDINAL INDICATOR
-  0x00ba,// #MASCULINE ORDINAL INDICATOR
-  0x00bf,// #INVERTED QUESTION MARK
-  0x2310,// #REVERSED NOT SIGN
-  0x00ac,// #NOT SIGN
-  0x00bd,// #VULGAR FRACTION ONE HALF
-  0x00bc,// #VULGAR FRACTION ONE QUARTER
-  0x00a1,// #INVERTED EXCLAMATION MARK
-  0x00ab,// #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
-  0x00bb,// #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
-  0x2591,// #LIGHT SHADE
-  0x2592,// #MEDIUM SHADE
-  0x2593,// #DARK SHADE
-  0x2502,// #BOX DRAWINGS LIGHT VERTICAL
-  0x2524,// #BOX DRAWINGS LIGHT VERTICAL AND LEFT
-  0x2561,// #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
-  0x2562,// #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
-  0x2556,// #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
-  0x2555,// #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
-  0x2563,// #BOX DRAWINGS DOUBLE VERTICAL AND LEFT
-  0x2551,// #BOX DRAWINGS DOUBLE VERTICAL
-  0x2557,// #BOX DRAWINGS DOUBLE DOWN AND LEFT
-  0x255d,// #BOX DRAWINGS DOUBLE UP AND LEFT
-  0x255c,// #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
-  0x255b,// #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
-  0x2510,// #BOX DRAWINGS LIGHT DOWN AND LEFT
-  0x2514,// #BOX DRAWINGS LIGHT UP AND RIGHT
-  0x2534,// #BOX DRAWINGS LIGHT UP AND HORIZONTAL
-  0x252c,// #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
-  0x251c,// #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
-  0x2500,// #BOX DRAWINGS LIGHT HORIZONTAL
-  0x253c,// #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
-  0x255e,// #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
-  0x255f,// #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
-  0x255a,// #BOX DRAWINGS DOUBLE UP AND RIGHT
-  0x2554,// #BOX DRAWINGS DOUBLE DOWN AND RIGHT
-  0x2569,// #BOX DRAWINGS DOUBLE UP AND HORIZONTAL
-  0x2566,// #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
-  0x2560,// #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
-  0x2550,// #BOX DRAWINGS DOUBLE HORIZONTAL
-  0x256c,// #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
-  0x2567,// #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
-  0x2568,// #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
-  0x2564,// #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
-  0x2565,// #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
-  0x2559,// #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
-  0x2558,// #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
-  0x2552,// #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
-  0x2553,// #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
-  0x256b,// #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
-  0x256a,// #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
-  0x2518,// #BOX DRAWINGS LIGHT UP AND LEFT
-  0x250c,// #BOX DRAWINGS LIGHT DOWN AND RIGHT
-  0x2588,// #FULL BLOCK
-  0x2584,// #LOWER HALF BLOCK
-  0x258c,// #LEFT HALF BLOCK
-  0x2590,// #RIGHT HALF BLOCK
-  0x2580,// #UPPER HALF BLOCK
-  0x03b1,// #GREEK SMALL LETTER ALPHA
-  0x00df,// #LATIN SMALL LETTER SHARP S
-  0x0393,// #GREEK CAPITAL LETTER GAMMA
-  0x03c0,// #GREEK SMALL LETTER PI
-  0x03a3,// #GREEK CAPITAL LETTER SIGMA
-  0x03c3,// #GREEK SMALL LETTER SIGMA
-  0x00b5,// #MICRO SIGN
-  0x03c4,// #GREEK SMALL LETTER TAU
-  0x03a6,// #GREEK CAPITAL LETTER PHI
-  0x0398,// #GREEK CAPITAL LETTER THETA
-  0x03a9,// #GREEK CAPITAL LETTER OMEGA
-  0x03b4,// #GREEK SMALL LETTER DELTA
-  0x221e,// #INFINITY
-  0x03c6,// #GREEK SMALL LETTER PHI
-  0x03b5,// #GREEK SMALL LETTER EPSILON
-  0x2229,// #INTERSECTION
-  0x2261,// #IDENTICAL TO
-  0x00b1,// #PLUS-MINUS SIGN
-  0x2265,// #GREATER-THAN OR EQUAL TO
-  0x2264,// #LESS-THAN OR EQUAL TO
-  0x2320,// #TOP HALF INTEGRAL
-  0x2321,// #BOTTOM HALF INTEGRAL
-  0x00f7,// #DIVISION SIGN
-  0x2248,// #ALMOST EQUAL TO
-  0x00b0,// #DEGREE SIGN
-  0x2219,// #BULLET OPERATOR
-  0x00b7,// #MIDDLE DOT
-  0x221a,// #SQUARE ROOT
-  0x207f,// #SUPERSCRIPT LATIN SMALL LETTER N
-  0x00b2,// #SUPERSCRIPT TWO
-  0x25a0,// #BLACK SQUARE
-  0x00a0,// #NO-BREAK SPACE
-  };
-
-  public char map_cp850_unicode(char x) {
-    if (x>=0x100)
-      return x;
-    return unimap[x];
-  }
-
-  private void _SetCursor(int row,int col) {
-    int maxr = display.getRows();
-    int tm = display.getTopMargin();
-
-    R = (row<0)?0:row;
-    C = (col<0)?0:col;
-
-    if (!moveoutsidemargins) {
-	R	+= display.getTopMargin();
-	maxr	 = display.getBottomMargin();
-    }
-    if (R>maxr) R = maxr;
-  }
-
-  public void putChar(char c,boolean doshowcursor) {
-    Dimension size;
-    int  rows = display.getRows() ; //statusline
-    int  columns = display.getColumns();
-    int  tm = display.getTopMargin();
-    int  bm = display.getBottomMargin();
-    String  tosend;
-    Vector  vec;
-    byte  msg[];
-
-    if (debug>4) System.out.println("putChar("+c+" ["+((int)c)+"]) at R="+R+" , C="+C+", columns="+columns+", rows="+rows);
-    display.markLine(R,1);
-    if (c>255) {
-      if (debug>0)
-        System.out.println("char > 255:"+((int)c));
-      return;
-    }
-    switch (term_state) {
-    case TSTATE_DATA:
-      /* FIXME: we shouldn't use chars with bit 8 set if ibmcharset.
-       * probably... but some BBS do anyway...
-       */
-      if (!useibmcharset) {
-        boolean doneflag = true;
-        switch (c) {
-        case OSC:
-          osc="";
-          term_state = TSTATE_OSC;
-          break;
-        case RI:
-          if (R>tm)
-            R--;
-          else
-            display.insertLine(R,1,display.SCROLL_DOWN);
-          if (debug>1)
-            System.out.println("RI");
-          break;
-        case IND:
-          if (R == tm - 1 || R == bm || R == rows - 1) //  Ray: not bottom margin - 1
-            display.insertLine(R,1,display.SCROLL_UP);
-          else
-            R++;
-          if (debug>1)
-            System.out.println("IND (at "+R+" )");
-          break;
-        case NEL:
-          if (R == tm - 1 || R == bm || R == rows - 1) //  Ray: not bottom margin - 1
-            display.insertLine(R,1,display.SCROLL_UP);
-          else
-            R++;
-          C=0;
-          if (debug>1)
-            System.out.println("NEL (at "+R+" )");
-          break;
-        case HTS:
-          Tabs[C] = 1;
-          if (debug>1)
-            System.out.println("HTS");
-          break;
-        case DCS:
-          dcs="";
-          term_state = TSTATE_DCS;
-          break;
-        default:
-          doneflag = false;
-          break;
-        }
-        if (doneflag) break;
-      }
-      switch (c) {
-      case CSI: // should be in the 8bit section, but some BBS use this
-        term_state = TSTATE_DCEQ;
-        break;
-      case ESC:
-        term_state = TSTATE_ESC;
-        lastwaslf=0;
-        break;
-      case '\b':
-        C--;
-        if (C<0)
-          C=0;
-        lastwaslf = 0;
-        break;
-      case '\t':
-        if (insertmode == 1) {
-          int  nr,newc;
-
-          newc = C;
-          do {
-            display.insertChar(C,R,' ',attributes);
-            newc++;
-          } while (newc<columns && Tabs[newc]==0);
-        } else {
-          do {
-            display.putChar(C++,R,' ',attributes);
-          } while (C<columns && (Tabs[C]==0));
-        }
-        lastwaslf = 0;
-        break;
-      case '\r':
-        C=0;
-        break;
-      case '\n':
-	if (debug>3)
-		System.out.println("R= "+R+", bm "+bm+", tm="+tm+", rows="+rows);
-        if (!vms)
-        {
-            if (lastwaslf!=0 && lastwaslf!=c)   //  Ray: I do not understand this logic.
-              break;
-            lastwaslf=c;
-            /*C = 0;*/
-        }
-	// note: we do not scroll at the topmargin! only at the bottommargin
-	// of the scrollregion and at the display bottom.
-	if ( R == bm || R >= rows - 1)
-	    display.insertLine(R,1);
-	else
-	    R++;
-        break;
-      case '\016':
-        /* ^N, Shift out - Put G1 into GL */
-        gl = 1;
-        break;
-      case '\017':
-        /* ^O, Shift in - Put G0 into GL */
-        gl = 0;
-        break;
-      default:
-        lastwaslf=0;
-        if (c<32) {
-          if (c!=0)
-            if (debug>0)
-              System.out.println("TSTATE_DATA char: "+((int)c));
-          break;
-        }
-        if(C >= columns) {
-          if(R < rows - 1)
-            R++;
-          else
-            display.insertLine(R,display.SCROLL_UP);
-          C = 0;
-        }
-
-        // Mapping if DEC Special is chosen charset
-        if ( gx[gl] == '0' ) {
-          if ( c >= '\u005f' && c <= '\u007e' ) {
-          if (debug>3)
-            System.out.print("Mapping "+c+" (index "+((short)c-0x5f)+" to ");
-          c = DECSPECIAL[(short)c - 0x5f];
-          if (debug>3)
-            System.out.println(c+" ("+(int)c+")");
-          }
-        }
-/*
-        if ( gx[gr] == '0' ) {
-          if ( c >= '\u00bf' && c <= '\u00fe' ) {
-          if (debug>2)
-            System.out.print("Mapping "+c);
-            c = DECSPECIAL[(short)c - 0xbf];
-            if (debug>2)
-              System.out.println("to "+c);
-          }
-        }
-*/
-        if (useibmcharset)
-          c = map_cp850_unicode(c);
-
-	/*if(true || (statusmode == 0)) { */
-	if (debug>4) System.out.println("output "+c+" at "+C+","+R);
-	  if (insertmode==1) {
-	    display.insertChar(C, R, c, attributes);
-	  } else {
-	    display.putChar(C, R, c, attributes);
-	  }
-	/*
-	} else {
-	  if (insertmode==1) {
-	    display.insertChar(C, rows, c, attributes);
-	  } else {
-	    display.putChar(C, rows, c, attributes);
-	  }
-	}
-	*/
-        C++;
-        break;
-      } /* switch(c) */
-      break;
-    case TSTATE_OSC:
-      if ((c<0x20) && (c!=ESC)) {// NP - No printing character
-        handle_osc(osc);
-        term_state = TSTATE_DATA;
-        break;
-      }
-      //but check for vt102 ESC \
-      if (c=='\\' && osc.charAt(osc.length()-1)==ESC) {
-        handle_osc(osc);
-        term_state = TSTATE_DATA;
-        break;
-      }
-      osc = osc + c;
-      break;
-    case TSTATE_ESC:
-      switch (c) {
-      case '#':
-        term_state = TSTATE_ESCSQUARE;
-        break;
-      case 'c':
-        /* Hard terminal reset */
-        /*FIXME:*/
-        term_state = TSTATE_DATA;
-        break;
-      case '[':
-        term_state = TSTATE_CSI;
-        DCEvar    = 0;
-        DCEvars[0]  = 0;
-        DCEvars[1]  = 0;
-        DCEvars[2]  = 0;
-        DCEvars[3]  = 0;
-        break;
-        case ']':
-        osc="";
-        term_state = TSTATE_OSC;
-        break;
-      case 'P':
-        dcs="";
-        term_state = TSTATE_DCS;
-        break;
-      case 'E':
-        if (R == tm - 1 || R == bm || R == rows - 1) //  Ray: not bottom margin - 1
-          display.insertLine(R,1,display.SCROLL_UP);
-        else
-          R++;
-        C=0;
-        if (debug>1)
-          System.out.println("ESC E (at "+R+")");
-        term_state = TSTATE_DATA;
-        break;
-      case 'D':
-        if (R == tm - 1 || R == bm || R == rows - 1)
-          display.insertLine(R,1,display.SCROLL_UP);
-        else
-          R++;
-        if (debug>1)
-          System.out.println("ESC D (at "+R+" )");
-        term_state = TSTATE_DATA;
-        break;
-      case 'M': // IL
-        if ((R>=tm) && (R<=bm)) // in scrolregion
-          display.insertLine(R,1,display.SCROLL_DOWN);
-	/* else do nothing ; */
-        if (debug>1)
-          System.out.println("ESC M ");
-        term_state = TSTATE_DATA;
-        break;
-      case 'H':
-        if (debug>1)
-          System.out.println("ESC H at "+C);
-        /* right border probably ...*/
-        if (C>=columns)
-          C=columns-1;
-        Tabs[C] = 1;
-        term_state = TSTATE_DATA;
-        break;
-      case '=':
-        /*application keypad*/
-        if (debug>0)
-          System.out.println("ESC =");
-        term_state = TSTATE_DATA;
-        break;
-      case '>':
-        /*normal keypad*/
-        if (debug>0)
-          System.out.println("ESC >");
-        term_state = TSTATE_DATA;
-        break;
-      case '7':
-        /*save cursor */
-        Sc = C;
-        Sr = R;
-        Sa = attributes;
-        if (debug>1)
-          System.out.println("ESC 7");
-        term_state = TSTATE_DATA;
-        break;
-      case '8':
-        /*restore cursor */
-        C = Sc;
-        R = Sr;
-        attributes = Sa;
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC 7");
-        break;
-      case '(':
-        /* Designate G0 Character set (ISO 2022) */
-        term_state = TSTATE_SETG0;
-        break;
-      case ')':
-        /* Designate G0 character set (ISO 2022) */
-        term_state = TSTATE_SETG1;
-        break;
-      case '*':
-        /* Designate G1 Character set (ISO 2022) */
-        term_state = TSTATE_SETG2;
-        break;
-      case '+':
-        /* Designate G1 Character set (ISO 2022) */
-        term_state = TSTATE_SETG3;
-        break;
-      case '~':
-        /* Locking Shift 1, right */
-        term_state = TSTATE_DATA;
-        gr = 1;
-        break;
-      case 'n':
-        /* Locking Shift 2 */
-        term_state = TSTATE_DATA;
-        gl = 2;
-        break;
-      case '}':
-        /* Locking Shift 2, right */
-        term_state = TSTATE_DATA;
-        gr = 2;
-        break;
-      case 'o':
-        /* Locking Shift 3 */
-        term_state = TSTATE_DATA;
-        gl = 3;
-        break;
-      case '|':
-        /* Locking Shift 3, right */
-        term_state = TSTATE_DATA;
-        gr = 3;
-        break;
-      default:
-        System.out.println("ESC unknown letter: ("+((int)c)+")");
-        term_state = TSTATE_DATA;
-        break;
-      }
-      break;
-    case TSTATE_SETG0:
-      if(c!='0' && c!='A' && c!='B')
-        System.out.println("ESC ( : G0 char set?  ("+((int)c)+")");
-      else {
-        if (debug>2) System.out.println("ESC ( : G0 char set  ("+c+" "+((int)c)+")");
-        gx[0] = c;
-      }
-      term_state = TSTATE_DATA;
-      break;
-    case TSTATE_SETG1:
-      if(c!='0' && c!='A' && c!='B')
-        System.out.println("ESC ) :G1 char set?  ("+((int)c)+")");
-      else {
-        if (debug>2) System.out.println("ESC ) :G1 char set  ("+c+" "+((int)c)+")");
-        gx[1] = c;
-      }
-      term_state = TSTATE_DATA;
-      break;
-    case TSTATE_SETG2:
-      if(c!='0' && c!='A' && c!='B')
-        System.out.println("ESC*:G2 char set?  ("+((int)c)+")");
-      else {
-        if (debug>2) System.out.println("ESC*:G2 char set  ("+c+" "+((int)c)+")");
-	gx[2] = c;
-      }
-      term_state = TSTATE_DATA;
-      break;
-    case TSTATE_SETG3:
-      if(c!='0' && c!='A' && c!='B')
-        System.out.println("ESC+:G3 char set?  ("+((int)c)+")");
-      else {
-        if (debug>2) System.out.println("ESC+:G3 char set  ("+c+" "+((int)c)+")");
-        gx[3] = c;
-      }
-      term_state = TSTATE_DATA;
-      break;
-    case TSTATE_ESCSQUARE:
-      switch (c) {
-      case '8':
-        for (int i=0;i<columns;i++)
-          for (int j=0;j<rows;j++)
-            display.putChar(i,j,'E',0);
-        break;
-      default:
-        System.out.println("ESC # "+c+" not supported.");
-        break;
-      }
-      term_state = TSTATE_DATA;
-      break;
-    case TSTATE_DCS:
-      if (c=='\\' && dcs.charAt(dcs.length()-1)==ESC) {
-        handle_dcs(dcs);
-        term_state = TSTATE_DATA;
-        break;
-      }
-      dcs = dcs + c;
-      break;
-    case TSTATE_DCEQ:
-      switch (c) {
-      case '0':
-      case '1':
-      case '2':
-      case '3':
-      case '4':
-      case '5':
-      case '6':
-      case '7':
-      case '8':
-      case '9':
-        DCEvars[DCEvar]=DCEvars[DCEvar]*10+((int)c)-48;
-        break;
-      case 'r': // XTERM_RESTORE
-        if (true || debug>1)
-          System.out.println("ESC [ ? "+DCEvars[0]+" r");
-        /* DEC Mode reset */
-        switch (DCEvars[0]){
-        case 3: /* 80 columns*/
-          size = display.size();
-          display.setWindowSize(80,rows);
-          layout();
-          break;
-        case 4: /* scrolling mode, smooth */
-          break;
-        case 5: /* light background */
-          break;
-        case 6: /* move inside margins ? */
-          moveoutsidemargins = true;
-          break;
-        case 12:/* local echo off */
-          break;
-        }
-        term_state = TSTATE_DATA;
-        break;
-      case 'h': // DECSET
-        if (debug>0)
-          System.out.println("ESC [ ? "+DCEvars[0]+" h");
-        /* DEC Mode set */
-        switch (DCEvars[0]){
-        case 1:  /* Application cursor keys */
-          KeyUp  = "\u001bOA";
-          KeyDown  = "\u001bOB";
-          KeyRight= "\u001bOC";
-          KeyLeft  = "\u001bOD";
-          break;
-        case 3: /* 132 columns*/
-          size = display.size();
-          display.setWindowSize(132,rows);
-          layout();
-          break;
-        case 4: /* scrolling mode, smooth */
-          break;
-        case 5: /* light background */
-          break;
-        case 6: /* move inside margins ? */
-          moveoutsidemargins = false;
-          break;
-        case 12:/* local echo off */
-          break;
-        }
-        term_state = TSTATE_DATA;
-        break;
-      case 'l':	//DECRST
-        /* DEC Mode reset */
-        if (debug>0)
-          System.out.println("ESC [ ? "+DCEvars[0]+" l");
-        switch (DCEvars[0]){
-        case 1:  /* Application cursor keys */
-          KeyUp  = "\u001b[A";
-          KeyDown  = "\u001b[B";
-          KeyRight= "\u001b[C";
-          KeyLeft  = "\u001b[D";
-          break;
-        case 3: /* 80 columns*/
-          size = display.size();
-          display.setWindowSize(80,rows);
-          layout();
-          break;
-        case 4: /* scrolling mode, jump */
-          break;
-        case 5: /* dark background */
-          break;
-        case 6: /* move outside margins ? */
-          moveoutsidemargins = true;
-          break;
-        case 12:/* local echo on */
-          break;
-        }
-        term_state = TSTATE_DATA;
-        break;
-      case ';':
-        DCEvar++;
-        DCEvars[DCEvar] = 0;
-        break;
-      case 'n':
-        if (debug>0)
-          System.out.println("ESC [ ? "+DCEvars[0]+" n");
-        switch (DCEvars[0]) {
-        case 15:
-          /* printer? no printer. */
-          host.send(((char)ESC)+"[?13n");
-          System.out.println("ESC[5n");
-          break;
-        default:break;
-        }
-        term_state = TSTATE_DATA;
-        break;
-      default:
-        if (debug>0)
-          System.out.println("ESC [ ? "+DCEvars[0]+" "+c);
-        term_state = TSTATE_DATA;
-        break;
-      }
-      break;
-    case TSTATE_CSI_DOLLAR:
-      switch (c) {
-      case '}':
-	System.out.println("Active Status Display now "+DCEvars[0]);
-	statusmode = DCEvars[0];
-	break;
-/* bad documentation?
-      case '-':
-	System.out.println("Set Status Display now "+DCEvars[0]);
-	break;
- */
-      case '~':
-	System.out.println("Status Line mode now "+DCEvars[0]);
-	break;
-      default:
-	System.out.println("UNKNOWN Status Display code "+c+", with Pn="+DCEvars[0]);
-	break;
-      }
-      term_state = TSTATE_DATA;
-      break;
-    case TSTATE_CSI:
-      switch (c) {
-      case '$':
-	term_state=TSTATE_CSI_DOLLAR;
-	break;
-      case '?':
-        DCEvar=0;
-        DCEvars[0]=0;
-        term_state=TSTATE_DCEQ;
-        break;
-      case '0':
-      case '1':
-      case '2':
-      case '3':
-      case '4':
-      case '5':
-      case '6':
-      case '7':
-      case '8':
-      case '9':
-        DCEvars[DCEvar]=DCEvars[DCEvar]*10+((int)c)-48;
-        break;
-      case ';':
-        DCEvar++;
-        DCEvars[DCEvar] = 0;
-        break;
-      case 'c':/* send primary device attributes */
-        /* send (ESC[?61c) */
-        host.send(((char)ESC)+"[?1;2c");
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" c");
-        break;
-      case 'q':
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" q");
-        term_state = TSTATE_DATA;
-        break;
-      case 'g':
-        /* used for tabsets */
-        switch (DCEvars[0]){
-        case 3:/* clear them */
-          int nw = display.getColumns();
-          Tabs = new byte[nw];
-          break;
-        case 0:
-          Tabs[C] = 0;
-          break;
-        }
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" g");
-        term_state = TSTATE_DATA;
-        break;
-      case 'h':
-        switch (DCEvars[0]) {
-        case 4:
-          insertmode = 1;
-          break;
-        case 20:
-          sendcrlf = true;
-          break;
-        default:
-          System.out.println("unsupported: ESC [ "+DCEvars[0]+" h");
-          break;
-        }
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" h");
-        break;
-      case 'l':
-        switch (DCEvars[0]) {
-        case 4:
-          insertmode = 0;
-          break;
-        case 20:
-          sendcrlf = false;
-          break;
-        }
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" l");
-        break;
-      case 'A': // CUU
-      {
-        int limit;
-	/* FIXME: xterm only cares about 0 and topmargin */
-        if (R > bm)
-            limit = bm+1;
-        else if (R >= tm) {
-            limit = tm;
-        } else
-            limit = 0;
-        if (DCEvars[0]==0)
-          R--;
-        else
-          R-=DCEvars[0];
-        if (R < limit)
-            R = limit;
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" A");
-        break;
-      }
-      case 'B':	// CUD
-        /* cursor down n (1) times */
-      {
-        int limit;
-        if (R < tm)
-            limit = tm-1;
-        else if (R <= bm) {
-            limit = bm;
-        } else
-            limit = rows - 1;
-        if (DCEvars[0]==0)
-          R++;
-        else
-          R+=DCEvars[0];
-        if (R > limit)
-            R = limit;
-        else {
-            if (debug>2) System.out.println("Not limited.");
-        }
-        if (debug>2) System.out.println("to: " + R);
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" B (at C="+C+")");
-        break;
-      }
-      case 'C':
-        if (DCEvars[0]==0)
-          C++;
-        else
-          C+=DCEvars[0];
-        if (C>columns-1)
-          C=columns-1;
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" C");
-        break;
-      case 'd': // CVA
-	R = DCEvars[0];
-        System.out.println("ESC [ "+DCEvars[0]+" d");
-        term_state = TSTATE_DATA;
-	break;
-      case 'D':
-        if (DCEvars[0]==0)
-          C--;
-        else
-          C-=DCEvars[0];
-        if (C<0) C=0;
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" D");
-        break;
-      case 'r': // DECSTBM
-        if (DCEvar>0)   //  Ray:  Any argument is optional
-        {
-          R = DCEvars[1]-1;
-          if (R < 0)
-            R = rows-1;
-          else if (R >= rows) {
-            R = rows - 1;
- 	  }
-        } else
-          R = rows - 1;
-        display.setBottomMargin(DCEvars[1]-1);
-        if (R >= DCEvars[0])
-        {
-          R = DCEvars[0]-1;
-          if (R < 0)
-            R = 0;
-        }
-        display.setTopMargin(DCEvars[0]-1);
-	_SetCursor(0,0);
-        if (debug>1)
-          System.out.println("ESC ["+DCEvars[0]+" ; "+DCEvars[1]+" r");
-        term_state = TSTATE_DATA;
-        break;
-      case 'G':  /* CUP  / cursor absolute column */
-	C = DCEvars[0];
-	System.out.println("ESC [ "+DCEvars[0]+" G");
-        term_state = TSTATE_DATA;
-	break;
-      case 'H':  /* CUP  / cursor position */
-        /* gets 2 arguments */
-	_SetCursor(DCEvars[0]-1,DCEvars[1]-1);
-        term_state = TSTATE_DATA;
-        if (debug>2) {
-          System.out.println("ESC [ "+DCEvars[0]+";"+DCEvars[1]+" H, moveoutsidemargins "+moveoutsidemargins);
-          System.out.println("	-> R now "+R+", C now "+C);
-	}
-        break;
-      case 'f':  /* move cursor 2 */
-        /* gets 2 arguments */
-        R = DCEvars[0]-1;
-        C = DCEvars[1]-1;
-        if (C<0) C=0;
-        if (R<0) R=0;
-        term_state = TSTATE_DATA;
-        if (debug>2)
-          System.out.println("ESC [ "+DCEvars[0]+";"+DCEvars[1]+" f");
-        break;
-      case 'L':
-        /* insert n lines */
-        if (DCEvars[0]==0)
-          display.insertLine(R,display.SCROLL_DOWN);
-        else
-          display.insertLine(R,DCEvars[0],display.SCROLL_DOWN);
-        term_state = TSTATE_DATA;
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" L (at R "+R+")");
-        break;
-      case 'M':
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+"M at R="+R);
-        if (DCEvars[0]==0)
-          display.deleteLine(R);
-        else
-          for (int i=0;i<DCEvars[0];i++)
-            display.deleteLine(R);
-        term_state = TSTATE_DATA;
-        break;
-      case 'K':
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" K");
-        /* clear in line */
-        switch (DCEvars[0]) {
-        case 0:/*clear to right*/
-          if (C<columns-1)
-            display.deleteArea(C,R,columns-C,1);
-          break;
-        case 1:/*clear to the left*/
-          if (C>0)
-            display.deleteArea(0,R,C,1);    // Ray: Should at least include character before this one, not C-1
-          break;
-        case 2:/*clear whole line */
-          display.deleteArea(0,R,columns,1);
-          break;
-        }
-        term_state = TSTATE_DATA;
-        break;
-      case 'J':
-        /* clear display.below current line */
-        switch (DCEvars[0]) {
-        case 0:
-          if (R<rows-1)
-            display.deleteArea(0,R + 1,columns,rows-R-1);
-          if (C<columns-1)
-            display.deleteArea(C,R,columns-C,1);
-          break;
-        case 1:
-          if (R>0)
-            display.deleteArea(0,0,columns,R-1);
-          if (C>0)
-            display.deleteArea(0,R,C,1);    // Ray: Should at least include character before this one, not C-1
-          break;
-        case 2:
-          display.deleteArea(0,0,columns,rows);
-          break;
-        }
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" J");
-        term_state = TSTATE_DATA;
-        break;
-      case '@':
-	if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" @");
-	for (int i=0;i<DCEvars[0];i++)
-	  display.insertChar(C,R,' ',attributes);
-	term_state = TSTATE_DATA;
-	break;
-      case 'P':
-        if (debug>1)
-          System.out.println("ESC [ "+DCEvars[0]+" P, C="+C+",R="+R);
-	if (DCEvars[0]==0) DCEvars[0]=1;
-	for (int i=0;i<DCEvars[0];i++)
-          display.deleteChar(C,R);
-        term_state = TSTATE_DATA;
-        break;
-      case 'n':
-        switch (DCEvars[0]){
-        case 5: /* malfunction? No malfunction. */
-          host.send(((char)ESC)+"[0n");
-          if(debug > 1)
-            System.out.println("ESC[5n");
-          break;
-        case 6:
-          host.send(((char)ESC)+"["+R+";"+C+"R");
-          if(debug > 1)
-            System.out.println("ESC[6n");
-          break;
-        default:
-          if (debug>0)
-            System.out.println("ESC [ "+DCEvars[0]+" n??");
-          break;
-        }
-        term_state = TSTATE_DATA;
-        break;
-      case 'm':  /* attributes as color, bold , blink,*/
-        if (debug>3)
-          System.out.print("ESC [ ");
-        if (DCEvar == 0 && DCEvars[0] == 0)
-          attributes = 0;
-        for (i=0;i<=DCEvar;i++) {
-          switch (DCEvars[i]) {
-          case 0:
-            if (DCEvar>0)
-              attributes =0;
-            break;
-          case 4:
-            attributes |= CharDisplay.UNDERLINE;
-            break;
-          case 1:
-            attributes |= CharDisplay.BOLD;
-            break;
-          case 7:
-            attributes |= CharDisplay.INVERT;
-            break;
-          case 5: /* blink on */
-            break;
-          case 25: /* blinking off */
-            break;
-          case 27:
-            attributes &= ~CharDisplay.INVERT;
-            break;
-          case 24:
-            attributes &= ~CharDisplay.UNDERLINE;
-            break;
-          case 22:
-            attributes &= ~CharDisplay.BOLD;
-            break;
-          case 30:
-          case 31:
-          case 32:
-          case 33:
-          case 34:
-          case 35:
-          case 36:
-          case 37:
-            attributes &= ~(0xf<<3);
-            attributes |= ((DCEvars[i]-30)+1)<<3;
-            break;
-          case 39:
-            attributes &= ~(0xf<<3);
-            break;
-          case 40:
-          case 41:
-          case 42:
-          case 43:
-          case 44:
-          case 45:
-          case 46:
-          case 47:
-            attributes &= ~(0xf<<7);
-            attributes |= ((DCEvars[i]-40)+1)<<7;
-            break;
-          case 49:
-            attributes &= ~(0xf<<7);
-            break;
-
-          default:
-            System.out.println("ESC [ "+DCEvars[i]+" m unknown...");
-            break;
-          }
-          if (debug>3)
-            System.out.print(""+DCEvars[i]+";");
-        }
-        if (debug>3)
-          System.out.print(" (attributes = "+attributes+")m \n");
-        term_state = TSTATE_DATA;
-        break;
-      default:
-        if (debug>0)
-          System.out.println("ESC [ unknown letter:"+c+" ("+((int)c)+")");
-        term_state = TSTATE_DATA;
-        break;
-      }
-      break;
-    default:
-      term_state = TSTATE_DATA;
-      break;
-    }
-    if (C > columns) C = columns;
-    if (R > rows)  R = rows;
-    if (C < 0)  C = 0;
-    if (R < 0)  R = 0;
-    if (doshowcursor)
-      display.setCursorPos(C, R);
-    display.markLine(R,1);
-  }
-}
diff --git a/web/root/telnet/examples/bbs.html b/web/root/telnet/examples/bbs.html
deleted file mode 100644
index 89375f178e..0000000000
--- a/web/root/telnet/examples/bbs.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet - Cereal BBS</TITLE>
-</HEAD>
-
-<H1>A JAVA Telnet Applet example - Cereal BBS </H1>
-
-<P>&copy; 1996 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>, 
-&copy; 1996 <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A> 
-<P>
-BBS Example one connects you to cereal.mv.com BBS after you pressed the button
-below. It supports full ANSI graphics and uses the appWrapper for maximum
-utilization of the web browser. After detaching the telnet window you can
-still go on browsing the web.
-<P>
-<APPLET CODEBASE="../" CODE="appWrapper.class" WIDTH=100 HEIGHT=25>
-<PARAM NAME=applet VALUE="telnet">
-<PARAM NAME=startButton VALUE="Connect to Cereal!">
-<PARAM NAME=stopButton VALUE="Shutdown Telnet">
-<PARAM NAME=frameTitle VALUE="The Java Telnet Applet: Example [cereal.mv.com]">
-<PARAM NAME=address VALUE="cereal.mv.com">
-<PARAM NAME=port VALUE="23">
-<PARAM NAME=emulation VALUE="vt320">
-<PARAM NAME=VTcolumns VALUE="80">
-<PARAM NAME=VTrows VALUE="24">
-<PARAM NAME=VTfont VALUE="Courier">
-<PARAM NAME=VTfontsize VALUE="12">
-<PARAM NAME=VTbuffer VALUE="1000">
-<PARAM NAME=VTscrollbar VALUE="true">
-<PARAM NAME=VTcharset VALUE="ibm">
-</APPLET>
-</HTML>
diff --git a/web/root/telnet/examples/bbs2.html b/web/root/telnet/examples/bbs2.html
deleted file mode 100644
index 09d5456472..0000000000
--- a/web/root/telnet/examples/bbs2.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet - The Rock Garden BBS</TITLE>
-</HEAD>
-
-<H1>A JAVA Telnet Applet example - The Rock Garden BBS</H1>
-
-<P>&copy; 1996 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>, 
-&copy; 1996 <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A> 
-<P>
-BBS example two. Connect to Rock Garden BBS with fill ANSI graphics character
-support. This example makes use of the appWrapper.
-<P>
-<APPLET CODEBASE="../" CODE="appWrapper.class" WIDTH=640 HEIGHT=480>
-<PARAM NAME=applet VALUE="telnet">
-
-<PARAM NAME=address VALUE="garden.hvs.com">
-<PARAM NAME=port VALUE="23">
-<PARAM NAME=emulation VALUE="vt320">
-<PARAM NAME=VTcolumns VALUE="80">
-<PARAM NAME=VTrows VALUE="24">
-<PARAM NAME=VTfont VALUE="Courier">
-<PARAM NAME=VTfontsize VALUE="12">
-<PARAM NAME=VTbuffer VALUE="1000">
-<PARAM NAME=VTscrollbar VALUE="true">
-<PARAM NAME=VTcharset VALUE="ibm">
-</APPLET>
-</HTML>
diff --git a/web/root/telnet/examples/bbs3.html b/web/root/telnet/examples/bbs3.html
deleted file mode 100644
index c64d6f208b..0000000000
--- a/web/root/telnet/examples/bbs3.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet - Connection Universe BBS</TITLE>
-</HEAD>
-
-<H1>A JAVA Telnet Applet example - Connection Universe BBS</H1>
-
-<P>&copy; 1996 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>, 
-&copy; 1996 <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A> 
-<P>
-BBS example three. It connects you to Connection Universe with full ANSI
-graphics character support. This example does not use the appWrapper. It
-is recommended, however.
-<P>
-<APPLET CODEBASE="../" CODE="telnet.class" WIDTH=640 HEIGHT=480>
-<PARAM NAME=address VALUE="www.cu-muc.de">
-<PARAM NAME=port VALUE="23">
-<PARAM NAME=emulation VALUE="vt320">
-<PARAM NAME=VTcolumns VALUE="80">
-<PARAM NAME=VTrows VALUE="24">
-<PARAM NAME=VTfont VALUE="Courier">
-<PARAM NAME=VTfontsize VALUE="12">
-<PARAM NAME=VTcharset VALUE="ibm">
-</APPLET>
-</HTML>
diff --git a/web/root/telnet/examples/lh.html b/web/root/telnet/examples/lh.html
deleted file mode 100644
index 61d288e889..0000000000
--- a/web/root/telnet/examples/lh.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet - localhost</TITLE>
-</HEAD>
-
-<H1>A JAVA Telnet Applet - localhost</H1>
-
-<P>&copy; 1996 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>, 
-&copy; 1996 <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A> 
-<P>
-Another example using the telnet applet only. Without the appWrapper it
-causes the applet to hange when you leave the page. It is customized with
-a buttonbar for usage on UNIX systems.
-<P>
-<APPLET CODEBASE="../" CODE="telnet.class" WIDTH=640 HEIGHT=480>
-<PARAM NAME=address VALUE="localhost">
-<PARAM NAME=port VALUE="23">
-<PARAM NAME=emulation VALUE="vt320">
-<PARAM NAME=VTcolumns VALUE="80">
-<PARAM NAME=VTrows VALUE="24">
-<PARAM NAME=VTfont VALUE="Courier">
-<PARAM NAME=VTfontsize VALUE="12">
-<PARAM NAME=VTbuffer VALUE="1000">
-<PARAM NAME=VTscrollbar VALUE="true">
-<PARAM NAME=1#Button VALUE="List Users|who\n">
-<PARAM NAME=2#Button VALUE="Execute:|sag \@exec@\n">
-<PARAM NAME=3#Input VALUE="exec#10|ps">
-<PARAM NAME=4#Button VALUE="Connect:|\$connect(\@addr@)">
-<PARAM NAME=5#Input VALUE="addr#20|localhost">
-<PARAM NAME=6#Button VALUE="Disconnect!|\$disconnect()">
-<PARAM NAME=script VALUE="login:|leo|TERM =|vt100">
-</APPLET>
-</HTML>
diff --git a/web/root/telnet/examples/mg.html b/web/root/telnet/examples/mg.html
deleted file mode 100644
index 2baa869f27..0000000000
--- a/web/root/telnet/examples/mg.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The Java(tm) Telnet Applet: Connect to MorgenGrauen</TITLE>
-</HEAD>
-<BODY>
-
-<H1>The Java<SUP>(tm)</SUP> Telnet Applet: Connect to MorgenGrauen</H1>
-
-<P>&copy; 1996-98 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>,
-<A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus Mei&szlig;ner</A> 
-<P>
-This is a variation with almost all available parameters set. Two modules
-(ButtonBar and Script) are used for customization. It will login you as
-guest in the MUD MorgenGrauen.
-<P>
-
-<APPLET CODEBASE="../" CODE="telnet.class" WIDTH=640 HEIGHT=480>
-  <PARAM NAME=address VALUE="mg.mud.de">
-  <PARAM NAME=port VALUE="23">
-
-  <!-- terminal emulation -->
-  <PARAM NAME=VTscrollbar VALUE="true">
-  <PARAM NAME=VTresize    VALUE="font">
-  <PARAM NAME=VTfont      VALUE="Courier">
-  <PARAM NAME=VTfontsize  VALUE="13">
-  <PARAM NAME=VTid        VALUE="vt320">
-
-  <!-- modules: #1 is a buttonbar -->
-  <PARAM NAME=module#1    VALUE="ButtonBar@North">
-  <PARAM NAME=1#Button    VALUE="Norden|norden\n\r">
-  <PARAM NAME=2#Button    VALUE="S\&uuml;den|sueden\n\r">
-  <PARAM NAME=3#Button    VALUE="Westen|westen\n\r">
-  <PARAM NAME=4#Button    VALUE="Osten|Osten\n\r">
-  <PARAM NAME=5#Button    VALUE="Anwesende|kwer\n\r">
-  <PARAM NAME=6#Button    VALUE="HILFE!|hilfe \@help@\n\r">
-  <PARAM NAME=7#Input     VALUE="help#20|">
-  <PARAM NAME=8#Button    VALUE="Verbinden|\$connect()">
-  <PARAM NAME=9#Button    VALUE="Abbrechen|\$disconnect()">
-  <PARAM NAME=10#Button   VALUE="Externes Fenster|\$detach()">
-  <PARAM NAME=11#Button   VALUE="Text senden:|\@send@\r\n">
-  <PARAM NAME=12#Input    VALUE="send#80|wer">
-
-	<!-- modules: #2 is a script -->
-  <PARAM NAME=module#2    VALUE="Script">
-  <PARAM NAME=script      VALUE="denn ?|gast|EMail|none|ch:|weiblich">
-
-</APPLET>
-</BODY>
-</HTML>
diff --git a/web/root/telnet/examples/tapp.html b/web/root/telnet/examples/tapp.html
deleted file mode 100644
index 9501a1a3ae..0000000000
--- a/web/root/telnet/examples/tapp.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet - TAPPMud</TITLE>
-</HEAD>
-</BODY>
-
-<H1>A JAVA Telnet Applet example - TAPPMud</H1>
-
-<P>&copy; 1996 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>,
-&copy; 1996 <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A>
-<P>
-This is a very small example with no customization. The telnet applet itself
-is loaded on the page. Leaving the page causes the applet to stop working,
-but no disconnecting from the remote host.<P>
-
-<APPLET CODEBASE="../" CODE="telnet.class" WIDTH=600 HEIGHT=400>
-<PARAM NAME=address VALUE="tapp.mud.de">
-</APPLET>
-</BODY>
-</HTML>
diff --git a/web/root/telnet/frame.class b/web/root/telnet/frame.class
deleted file mode 100644
index 24cd6b269e1c8e46fae41295679c9717c22e25da..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 817
zcmX^0Z`VEsW(Hjb6Ltnwb_Q*B1|4<=U3LaNb_RVe237`ME(SIRJ}w4k27XQk76t(>
z1||kU5KD-ML7YL1he4S^n1?}~L4=1vi$Riy!2rZHWRPWKVAjwKV`LE5(2VxUN-Rs%
zPb@Fdch1i($j?j7E3sx|5Y|A5yOx2(G@}?9M4_TNiFxVz!6ikRdFj?5bJ%P$^D;~9
z7#Ucc^HWk885lho8Q6W&iV|~Etr;2keKPY>{YrC_Qj0<olX6lS83fP`XJin92!T!Z
zPs&P72Dt)R5NaJrWqxvEPFP}5Cde?TiCn??rA5i9ZkZtCMR9nFB{@I0fRTYcC9}97
zzc`hVfjPCTgpq+2>`+DqE-<YJ3J*pG-t^QGcz_f$GH_=k=B4DMg2RH5fiaVjfhjYE
zkwFNgT0gO%ASbm%-w{l~1;Ig@SCW{Sms-Tgzz0!TUZM{*10moB3MfVfez=P8%)FHR
zaz+N0;*$IVMh3Q$#G>@n5=I7=l8nq^Mh4cB%#xf`21N!&237_TU}9hd#Ww>Zg9HNu
zgB1f40}}%S1Dlrib_Vv13=9lR4ALMz10w?ig93v*0|SE?L<NHpgA~XB1~vv`1~~>s
z21W)A22rRvD;Ss<SQr=>9JIGE@MtY#u+-kdz$M6Vash+B);0!CU!8p{DR#>l<aA_>
z4l*#!WDwGkH9Ek+w2gsVYa0Ux%bFJE|FU!bKVe=2cYzuMCj$e6GJ_C<3WGR<DuX<O
z8iNLdI)fpD27@VsCWASHDuW$^4%mfS5WC?nbYM_nkOBKsok58~k%5UphCzuzlR=V!
MnSqf(0c?jF0H)omi~s-t

diff --git a/web/root/telnet/frame.java b/web/root/telnet/frame.java
deleted file mode 100644
index d30d5a19d5..0000000000
--- a/web/root/telnet/frame.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * frame -- a frame subclass for handling frame events
- * --
- * $Id: frame.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Tue Jul  8 10:02:36 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-import java.awt.Frame;
-import java.awt.Component;
-import java.awt.Event;
-import java.applet.Applet;
-
-public class frame extends Frame 
-{
-  public frame(String title) { super(title); }
-  
-  public boolean handleEvent(Event evt) {
-    if(evt.target == this && evt.id == Event.WINDOW_DESTROY) {
-      Component comp[] = getComponents();
-      for(int i = comp.length - 1; i >= 0; i--)
-        if(comp[i] instanceof Applet) {
-          ((Applet)comp[i]).stop();
-          this.dispose();
-          return true;
-        }
-    }
-    return false;
-  }
-}
-
-    
-    
diff --git a/web/root/telnet/ibmtest.html b/web/root/telnet/ibmtest.html
deleted file mode 100644
index 0140ce6e02..0000000000
--- a/web/root/telnet/ibmtest.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>The JAVA Telnet Applet</TITLE>
-</HEAD>
-
-<H1>The JAVA Telnet Applet</H1>
-
-<P>&copy; 1996 <A HREF="mailto:leo@first.gmd.de">Matthias L. Jugel</A>, 
-&copy; 1996 <A HREF="mailto:msmeissn@cip.informatik.uni-erlangen.de">Marcus 
-Mei&szlig;ner</A> 
-
-<APPLET CODE="telnet.class" WIDTH=640 HEIGHT=480>
-<PARAM NAME=address VALUE="tanis.first.gmd.de">
-<PARAM NAME=port VALUE="23">
-<PARAM NAME=emulation VALUE="vt320">
-<PARAM NAME=VTcolumns VALUE="80">
-<PARAM NAME=VTrows VALUE="24">
-<PARAM NAME=VTfont VALUE="Courier">
-<PARAM NAME=VTfontsize VALUE="12">
-<PARAM NAME=VTbuffer VALUE="1000">
-<PARAM NAME=VTscrollbar VALUE="true">
-<PARAM NAME=VTcharset VALUE="ibm">
-<PARAM NAME=1#Button VALUE="List Users|who\n">
-<PARAM NAME=2#Button VALUE="Execute:|sag \@exec@\n">
-<PARAM NAME=3#Input VALUE="exec#10|ps">
-<PARAM NAME=4#Button VALUE="Connect:|\$connect(\@addr@)">
-<PARAM NAME=5#Input VALUE="addr#20|localhost">
-<PARAM NAME=6#Button VALUE="Disconnect!|\$disconnect()">
-<PARAM NAME=script VALUE="login:|leo|TERM =|vt100">
-</APPLET>
-</HTML>
diff --git a/web/root/telnet/index.download.html b/web/root/telnet/index.download.html
deleted file mode 100644
index 27e680d493..0000000000
--- a/web/root/telnet/index.download.html
+++ /dev/null
@@ -1,182 +0,0 @@
-<!doctype HTML public "-//W30//DTD W3 HTML 3.0//EN">
-<HTML>
-  <HEAD>
-    <TITLE>The Java(tm) Telnet Applet: Downloading and Unpacking</TITLE>
-  </HEAD>
-  <BODY BGCOLOR="#ffffff">
-    <H1>The <A HREF="http://java.sun.com/">Java<SUP>(tm)</SUP></A>
-      Telnet Applet: Download</H1>
-    &copy; 1996, 97 <A HREF="mailto:leo@mud.de">Matthias L. Jugel</A>,
-    <A HREF="mailto:marcus@mud.de">Marcus Mei&szlig;ner</A> 
-    <HR>
-    <H2 ALIGN=CENTER>
-      [<A HREF="#package">Package</A> | <A HREF="#class">Classes</A>]
-    </H2>
-    <HR>
-    <P>
-      <A NAME="package"></A>
-    <H2>Package download and Unpacking</H2>
-    Select the package according to your operating system. Apple users may
-    choose to select the <A HREF="../telnet.zip">telnet.zip</A> file. Please be
-    aware that the packages contain <I>long filenames</I> and may not work with
-    systems not supporting these, such as Windows 3.x.<P>
-      If you have problems unpacking one of the packages read the notes below the
-      table or <A HREF="mailto:leo@mud.de,marcus@mud.de">contact us</A> if it doesn't make
-      sense.
-    <P>
-    <CENTER>
-      <TABLE BORDER=1>
-	<TR ALIGN=CENTER>
-	  <TD><TD><B>UNIX</B> <TD><B>Windows NT/95 &amp; Apple</B></TD>
-	<TR ALIGN=CENTER>
-	  <TD><B>full package</B>
-	  <TD><A HREF="../telnet.tgz">telnet.tgz</A></TD>
-	  <TD><A HREF="../telnet.zip">telnet.zip</A></TD>
-	<TR ALIGN=CENTER>
-	  <TD><B>classes only</B>
-	  <TD><A HREF="../classes.tgz">classes.tgz</A></TD>
-	  <TD><A HREF="../classes.zip">classes.zip</A></TD>
-	<TR ALIGN=CENTER>
-	<TR ALIGN=CENTER>
-	  <TD><B>patches</B>
-	  <TD COLSPAN=2>
-	    <A HREF="../telnet-srp-diff.tar.gz">secure authentication</A><BR>
-	  </TD>
-	<TR ALIGN=CENTER>
-	  <TD><B>full package</B>
-	  <TD><A HREF="../telnet_p.tgz">telnet_p.tgz</A></TD>
-	  <TD><A HREF="../telnet_p.zip">telnet_p.zip</A></TD>
-	<TR ALIGN=CENTER>
-	  <TD><B>classes only</B>
-	  <TD><A HREF="../class_p.tgz">class_p.tgz</A></TD>
-	  <TD><A HREF="../class_p.zip">class_p.zip</A></TD>
-      </TABLE>
-      <P>
-	<I>Note:</I> <A HREF="http://srp.stanford.edu/srp/"><B>Secure 
-	    Remote Password Authentication (SRP)</B></A>, &copy;  
-	    <A HREF="http://www.stanford.edu">Stanford University</A>,
-	    <A HREF="http://srp.stanford.edu/srp/export/eps-1.3.tar.gz">
-	    official distribution</A><BR> download the <B>patched</B> 
-	    version if you don't want to do it yourself.<P>
-	<I>Note:</I> We recommend you download the classes.zip in any case and
-	use the <BR><TT>&lt;APPLET ARCHIVE="classes.zip" ...&gt;</TT> parameter.<BR>
-	This <B>decreases the download time</B> for the applet!
-      <P>
-      <TABLE BORDER=1>
-	<TR ALIGN=CENTER>
-	  <TD><TD><B>Linux</B> <TD><B>Windows NT</B></TD>
-	<TR ALIGN=CENTER>
-	  <TD><B>simple relayd</B>
-	  <TD><A HREF="tools/relayd">relayd</A></TD>
-	  <TD><A HREF="tools/relayd.exe">relayd.exe</A></TD>
-	<TR ALIGN=CENTER>
-	  <TD><B>multi relayd</B>
-	  <TD><A HREF="tools/mrelayd">mrelayd</A></TD>
-	  <TD><A HREF="tools/mrelayd.exe">mrelayd.exe</A></TD>
-      </TABLE>
-      <P>
-	<I>Note:</I> Do a <B>chmod +x relayd</B> after download to make the
-	             file executable.<BR>If you need a binary for a different
-	             platform <A HREF="mailto:leo@mud.de,marcus@mud.de">please
-	             ask.</A>
-    </CENTER>
-    <P>
-    <DL>
-      <DT><B>Apple Computers:</B><P>
-      <DD>Unless you have a UNIX tar and gzip implementation download the
-	<A HREF="../telnet.zip">telnet.zip</A> file.<P>
-	  <DT><B>Windows NT/95:</B><P>
-	  <DD>Select the <A HREF="../telnet.zip">telnet.zip</A> file and unpack it
-	    using one of the publicly available tools, like
-	    <A HREF="http://www.winzip.com">WinZIP</A>. Make sure you have selected
-	    the checkbox to <B>extract subdirectories</B>!<P>
-	      <DT><B>UNIX:</B><P>
-	      <DD>Extract the files from the <A HREF="../telnet.tgz">telnet.tgz</A> file
-		using the following command:
-		<PRE><B>gunzip -c telnet.tgz | tar xvf -</B></PRE>
-		This will extract a directory called <B>Telnet/</B> containing the
-		full source code + documentation and precompiled classes. <B>gunzip</B>
-		(GNU unzip) can be found on a public ftp server.<P>
-		</DL>
-		  For further information on using and installing the applet please refer to the
-		  <A HREF="Documentation/index.html">Documentation</A>.
-		<HR>
-		<A NAME="class"></A>
-    <H2>Direct CLASS download:</H2>
-    Sometimes it is not necessary to download the complete package just to update
-    one class. Below is the tree of classes to directly download the class you
-    may need. If you are not sure, which classes have changed have a look at
-    your copy of the file REVISION and the same file on the home page of the
-    applet. If you are viewing this page from your own copy,
-    <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet/index.download.html">
-      go to the home page download</A>. Higher revision numbers indicate changes.
-    <P>
-      Right click on the link and select <I>Save as ...</I>. Please make sure you
-      download all needed classes. If you're downloading the first time, please
-      use the <A HREF="#package">package download</A> links above!
-    <P>
-      If you want all classes at once download either 
-      <A HREF="../classes.tgz">classes.tgz</A> (<B>UNIX</B>) or
-      <A HREF="../classes.zip">classes.zip</A> (<B>Windows/Apple</B>).
-    <P>
-    <UL>
-      <LI><A HREF="appWrapper.class">appWrapper.class</A> (applet wrapper)
-      <LI><A HREF="frame.class">frame.class</A> (appWrapper needs it!)<P>
-      <LI><B><A HREF="telnet.class">telnet.class</A></B> (applet/application)
-	<UL>
-	  <LI><A HREF="display/CharDisplay.class">display/CharDisplay.class</A>
-	    (character display class)
-	  <LI><A HREF="display/SoftFont.class">display/SoftFont.class</A>
-	    (software font for special characters)
-	  <LI><A HREF="display/Terminal.class">display/Terminal.class</A>
-	    (abstract terminal)
-	  <LI><A HREF="display/TerminalHost.class">display/TerminalHost.class</A>
-	    (abstract host interface)
-	  <LI><A HREF="display/vt320.class">display/vt320.class</A>
-	    (VT/ANSI terminal emulation)
-	    <P>
-	  <LI><A HREF="socket/TelnetIO.class">socket/TelnetIO.class</A>
-	    (telnet io handling)
-	  <LI><A HREF="socket/StatusPeer.class">socket/StatusPeer.class</A>
-	    (status peer interface)
-	    <P>
-	  <LI><A HREF="modules/Module.class">modules/Module.class</A>
-	    (abstract module interface)
-	  <LI><A HREF="modules/ButtonBar.class">modules/ButtonBar.class</A>
-	    (programmable button bar)
-	  <LI><A HREF="modules/Script.class">modules/Script.class</A>
-	    (simple script module)
-	  <LI><A HREF="modules/MudConnector.class">modules/MudConnector.class</A>
-	    (a specialized specialized example module)
-	  <LI><A HREF="modules/BSXModule.class">modules/BSXModule</A> 
-	    (a graphical MUD extension)
-	  <UL>
-	      <LI><A HREF="modules/bsx/BSXDisplay.class">modules/bsx/BSXDisplay.class</A>
-	      <LI><A HREF="modules/bsx/BSXGraphic.class">modules/bsx/BSXGraphic.class</A>
-	      <LI><A HREF="modules/bsx/BSXInputStream.class">modules/bsx/BSXInputStream.class</A>
-	      <LI><A HREF="modules/bsx/BSXObject.class">modules/bsx/BSXObject.class</A>
-	      <LI><A HREF="modules/bsx/BSXPolygon.class">modules/bsx/BSXPolygon.class</A>
-	      <LI><A HREF="modules/bsx/BSXScene.class">modules/bsx/BSXScene.class</A>
-	   </UL>
-	</UL><P>
-      <LI><B>additional tools:</B>
-	<UL>
-	  <LI><A HREF="tools/proxy.class">tools/proxy.class</A> (application)<BR>
-	    Standalone program to be run as server for telnet redirection.
-	  <LI><A HREF="tools/redirector.class">tools/redirector.class</A>
-	    (internal redirector)
-	</UL><P>
-      <LI><A HREF="IOtest.class">IOtest.class</A> (application)
-      <LI><A HREF="TelnetWrapper.class">TelnetWrapper.class</A> (application)
-      <LI><A HREF="CharDisplayTest.class">CharDisplayTest.class</A>
-    </UL>		
-    <HR>
-    <A HREF="index.html">
-      <IMG ALIGN=LEFT SRC="Documentation/images/left.gif" BORDER=0 ALT="[BACK]"></A>
-    <B>Get the <A HREF="http://www.first.gmd.de/persons/leo/java/Telnet">latest
-	version</A> here!</B> <BR>
-    <!-- html-ts start -->
-    Last modified: Wed Jul 23 14:46:07 1997 by Matthias L. Jugel
-    <!-- html-ts end -->
-  </BODY>
-</HTML>
diff --git a/web/root/telnet/index.ssjs b/web/root/telnet/index.ssjs
deleted file mode 100644
index d9889e380d..0000000000
--- a/web/root/telnet/index.ssjs
+++ /dev/null
@@ -1,2 +0,0 @@
-load(web_root_dir + "/../lib/template.ssjs");
-write_template("telnet.inc");
\ No newline at end of file
diff --git a/web/root/telnet/modules/BSXModule.class b/web/root/telnet/modules/BSXModule.class
deleted file mode 100644
index 7dbf91c7379142919954dd2d85e7b756b06668ea..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 10405
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
AMF0Q*

diff --git a/web/root/telnet/modules/BSXModule.java b/web/root/telnet/modules/BSXModule.java
deleted file mode 100644
index 5ca21fc16e..0000000000
--- a/web/root/telnet/modules/BSXModule.java
+++ /dev/null
@@ -1,562 +0,0 @@
-/**
- * BSXModule -- implements BSX controll sequence handling
- * --
- * $Id: BSXModule.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Tue Oct 14 18:00:00 1997 by Thomas Kriegelstein :$
- *
- * This file and the related package may be part of "The Java Telnet
- * Applet" by Matthias L. Jugel.
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The BSXModule" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-package modules;
-
-import telnet;
-import frame;
-
-import modules.bsx.*;
-import java.applet.Applet;
-import java.awt.*;
-import java.io.*;
-import java.util.*;
-/**
- * The ultimate BSX module implements most of the common used
- * BSX controll sequences.
- * <P>
- * Features:<UL>
- * <LI>a Statemachine to parse the Strings passed by the telnet
- * <LI>a own package named bsx, for Window and Polygon handling
- * terrible english because my native language is german
- * </UL>
- * TODO:<UL>
- * <LI>more BSX Sequences
- * <LI>Documentaion
- * </UL>
- * 
- * @author  Thomas Kriegelstein
- */
-
-public class BSXModule extends Panel implements Module 
-{
-  /** Client Version */
-  protected String clientVersion = "Foob 0.5";
-  /** The Frame, where the graphic is drawn */
-  private BSXDisplay bsxWindow;
-  /** Our parent, the telnet app. */
-  private telnet parent;
-  /** the Container, wich is around us */
-  private Container toplevel;
-  /** the Buttons to switch on and off the logging and BSX handling */
-  private Button bsxButton,logButton;
-  /** the states of the Buttons */
-  private boolean bsxCheckIt,logging;
-  /** register o as our parent */ 
-  public void setLoader(Object o) { parent = (telnet)o; }
-  /** do nothing */
-  public void connect(String host, int port) {}
-  /** do nothing */
-  public void disconnect() {}
-  /**
-   * This method is called by the telnet, so that we can parse the String.
-   * Prints out the filtered String if logging s enabled.
-   * @param String s the String to be parsed
-   * @return a filtered String with no BSX sequences in it
-   */
-  public String receive(String s) 
-    {
-      String res;
-      if (!bsxCheckIt) res=s;
-      else res=parseString(s);
-      if (logging)
-	System.out.println(res);
-      return res;
-    }
-  /**
-   * Adds two Buttons to the Panel and instanciates a BSXWindow.
-   */   
-  public void addNotify() 
-    {
-      this.setLayout(new GridLayout(1,0));
-      // Enable BSX Parsing
-      if (bsxWindow==null)
-	{
-	  this.add(bsxButton=new Button("BSX aus!"));
-	  bsxCheckIt=true;
-	  this.add(logButton=new Button("Logging an!"));
-	  logging=false;
-	  bsxWindow = new BSXDisplay();
-	}
-      //
-      super.addNotify();
-    }
-  /**
-   * Java 1.0 eventhandling routines.
-   * @param Event e
-   * @return true if Event has been recognized and fullfilled.
-   */
-  public boolean handleEvent(Event e)
-    {
-      if ((e.id==Event.ACTION_EVENT)&&(e.target==bsxButton)) 
-	{
-	  bsxCheckIt=!bsxCheckIt;
-	  if (bsxCheckIt) 
-	    {
-	      parent.writeToSocket("#VER "+clientVersion+"\r\n");
-	      bsxButton.setLabel("BSX aus!");
-	      bsxWindow.show();
-	    }
-	  else 
-	    {
-	      parent.writeToSocket("#VER\r\n");
-	      bsxButton.setLabel("BSX an!");
-	      bsxWindow.hide();
-	      if (debug>1)
-		{
-		  parent.writeToUser(lastCommand+strIdentifier+BSXData);
-		  System.out.println("BSX off: last Input -");
-		  System.out.println(lastCommand+strIdentifier+BSXData);
-		}
-	    }
-	  resetMachine();  
-	  return true;
-	}
-      if ((e.id==Event.ACTION_EVENT)&&(e.target==logButton))
-	{
-	  logging=!logging;
-	  if (logging)
-	    {
-	      logButton.setLabel("Logging aus!");
-	    }
-	  else
-	    {
-	      logButton.setLabel("Logging an!");
-	    }
-	  return true;
-	}
-      return super.handleEvent(e);
-    }
-
-  /** 
-   * And here we go again * latest implementation of my StateMachine
-   * all methods that return a String, return the String passed to them
-   * but without the chars they have recognized and solved
-   * the real results are contained in the (global) variables
-   */
-
-  /**
-   * my Debuglevel, because this software is still beta.
-   */
-  private static int debug = 0;
-  /**
-   * a main for test-purposes
-   */
-  public static void main (String args[])
-  {
-    BSXModule bsxm=new BSXModule();
-    debug=1;
-    String res;
-    res=bsxm.parseString("Testing@Hawk@RQV@SCEintro.@VIO/magier:foobar.@RFS@RMO/magier:foo");
-    res+=bsxm.parseString("bar.@RFSThis is a test.@TMS@PRO@RFS@BOMTest me Carefully.");
-    System.out.println("Gefiltert: "+res);
-  }
-  /**
-   * lastCommand is a Statedescriptor.<BR>
-   * if lastCommand.length()=4 then the entire Command has been read
-   * else go on reading the command completly<BR>
-   * Commands are: PRO SCE VIO DFS DFO TMS RMO RFS RQV PUR
-   * additional commands are: BOM EOM LON LOF TXT which will not be supported 
-   * by Regenbogen BSX java Client.<BR>
-   * Commands are preceeded by a @
-   */
-  private String lastCommand="";
-  /**
-   * lastState identifies what we have done last time we received a string
-   * <UL>
-   * <LI>-1 undefined -> used to mark succesful work
-   * <LI> 0 nothing -> go on searching for a new command
-   * <LI> 1 identifier -> we have to complete the identifier
-   * <LI> 2 download -> we have to get more BSXData
-   * </UL>
-   */
-  private int lastState;
-  /** holds the identifier of the current object */
-  private String strIdentifier="";
-  /** holds the X position of the current object as a String*/
-  private String strXPos="";
-  /** holds the X position of the current object as an int */
-  private int intXPos=0;
-  /** holds the Y position of the current object as a String*/
-  private String strYPos="";
-  /** holds the Y position of the current object as an int */
-  private int intYPos=0;
-  /** holds the number of polygons in the current description as a String */
-  private String strPolys="";
-  /** holds the number of polygons in the current description as an int */
-  private int intPolys=0;
-  /** holds the number of edges in the current polygon as a String */
-  private String strEdges="";
-  /** holds the number of edges in the current polygon as an int */
-  private int intEdges=0;
-  /** holds the BSX description, including the numbers of polygons and edges */
-  private String BSXData="";
-  /** 
-   * reset machine to an initial status
-   */ 
-  private void resetMachine()
-  {
-    if (debug>0)
-    {
-      System.err.println("BSXStateMachine statustrace:");
-      System.err.println("Command:    "+lastCommand);
-      System.err.println("State:      "+lastState);
-      System.err.println("Identifier: "+strIdentifier);
-    }
-    lastCommand="";
-    lastState=0;
-    strIdentifier="";
-    strXPos="";
-    intXPos=0;
-    strYPos="";
-    intYPos=0;
-    strPolys="";
-    intPolys=0;
-    strEdges="";
-    intEdges=0;
-    BSXData="";
-  }
-  /**
-   * convert a Hexnumber contained in a 2byteString into int
-   * @param String s
-   * @return the converted int or 0 if not succesful 
-   */
-  private int asciiHexToInt(String s)
-  {
-    if (s.length()==2)
-    {
-      try
-      {
-        int h=(int)s.charAt(0);
-        if (h>='A') h-='A'-10; else h-='0';
-        int l=(int)s.charAt(1);
-        if (l>='A') l-='A'-10; else l-='0';
-	if (debug>9) warn("Converting Hex: "+s+" to int: "+(h*16+l));
-        return h*16+l;
-      }
-      catch (Exception e)
-      {
-        return 0;
-      }
-    }
-    return 0;
-  }
-  /**
-   * The method called to start the parsing
-   * @param String s
-   * @return filtered String
-   */
-  private String parseString(String s)
-  {
-    if ((s.indexOf("@")==-1)&&(lastState==0))
-    {
-      if (debug>0) warn("parseString done");
-      return s;
-    }
-    if (lastState!=0)
-    {
-      if (debug>0) warn("parseString pending Input");
-      s=decodeCommand(s);
-    }
-    int i=0;
-    int pos=s.indexOf("@",i);
-    String res="";
-    while ((pos!=-1)&&(pos<s.length()))
-    {
-      if (debug>0) warn("parseString Command at "+pos);
-      res+=s.substring(0,pos);
-      if (debug>0) warn("still to parse: "+s.substring(pos));
-      String tmp=decodeCommand(s.substring(pos));
-      if (tmp.equals(s.substring(pos))) i++;
-      else i=0;
-      s=tmp;
-      pos=s.indexOf("@",i); // search for next Command
-    }
-    res+=s;
-    return res;
-  }
-  /**
-   * simple messaging for debug purposes
-   * @param String s to write to stderr
-   */
-  private void warn(String s) { System.err.println("Warning: "+s+"."); }
-  /**
-   * read the identifier into strIdentifier
-   * @param String s to be parsed
-   * @returns String without the first identifier
-   */
-  private String readIdentifier(String s)
-  {
-    if (strIdentifier.equals("")) lastState=1;
-    if (lastState==1)
-    {
-      int i=s.indexOf(".");
-      if (i==-1)
-      {
-        strIdentifier+=s;
-        return "";
-      }
-      else
-      {
-        lastState=-1;
-        strIdentifier+=s.substring(0,i);
-        return s.substring(i+1);
-      }
-    }
-    return s;
-  }
-  /**
-   * read the BSX-Descriptions into BSXData
-   * @param String s to be parsed
-   * @return String without the first BSX Data sequence
-   */
-  private String readBSXData(String s)
-  {
-    if (strPolys.equals("")) lastState=2;
-    if (lastState==2)
-    {
-      while((strPolys.length()<2)&&(s.length()>0))
-      {
-        BSXData+=s.charAt(0);
-        strPolys+=s.charAt(0); s=s.substring(1);
-      }
-      if (strPolys.length()<2) return "";
-      else if (debug>1) warn("PolyCount read:"+strPolys);
-      if (intPolys == 0) 
-	intPolys=asciiHexToInt(strPolys);
-      if (debug>9) warn("equals "+intPolys);
-      while ((s.length()>0)&&(intPolys>0))
-      {
-        while((strEdges.length()<2)&&(s.length()>0))
-        {
-          BSXData+=s.charAt(0);
-          strEdges+=s.charAt(0); s=s.substring(1);
-        }
-        if (strEdges.length()<2) return "";
-	else if (debug>1) warn("EdgeCount read:"+strEdges);
-	if (intEdges == 0)
-	  intEdges=(asciiHexToInt(strEdges)*2+1)*2;
-        // color plus every edge x,y times 2 is the number of chars to read.
-        while((s.length()>0)&&(intEdges>0))
-        {
-          BSXData+=s.charAt(0); s=s.substring(1);
-          intEdges--;
-        }
-        if (intEdges>0) return "";
-        strEdges=""; // reset the number of Edges!
-        intPolys--;
-      }
-      if (intPolys>0) return "";
-      if (intPolys==0) strPolys="";
-      lastState=-1;
-    }
-    return s;
-  }
-  /**
-   * read the Command, catch all necessary data, and execute it
-   * @param String s to be parsed
-   * @return String without first command
-   */
-  private String decodeCommand(String s)
-  {
-    while ((lastCommand.length()!=4)&&(s.length()>0))
-    {
-      lastCommand+=s.charAt(0); s=s.substring(1);
-    }
-    if (lastCommand.length()!=4)
-    {
-      lastState=-1;
-      return "";
-    }
-    if (false) {} // hehe, just to write everytime "else if"
-    // ReFresh Scene
-    else if (lastCommand.equals("@RFS"))
-      {
-        resetMachine();
-        if (bsxWindow!=null) bsxWindow.repaint();
-        else warn("No BSX Output Available");
-      }
-    // VIsualize Object
-    else if (lastCommand.equals("@VIO"))
-      {
-        s=readIdentifier(s);
-        if (lastState==1) return "";
-        while ((strXPos.length()<2)&&(s.length()>0))
-        {
-          strXPos+=s.charAt(0); s=s.substring(1);
-        }
-        if (strXPos.length()!=2)
-        {
-          return "";
-        }
-        while ((strYPos.length()<2)&&(s.length()>0))
-        {
-          strYPos+=s.charAt(0); s=s.substring(1);
-        }
-        if (strYPos.length()!=2)
-        {
-          return "";
-        }
-	intXPos=asciiHexToInt(strXPos);
-	intYPos=asciiHexToInt(strYPos);
-        if (bsxWindow!=null)
-          if (!bsxWindow.showObject(strIdentifier,intXPos,intYPos))
-            if (parent!=null)
-              parent.writeToSocket("#RQO "+strIdentifier+"\r\n");
-            else warn("no Socket to send to");
-          else
-            if (debug>0)
-              System.out.println("Query BSX-Description for "+strIdentifier);
-            else ;
-        else warn("no BSX Output available");
-        resetMachine();
-      }
-    // ReMove Object
-    else if (lastCommand.equals("@RMO"))
-      {
-        s=readIdentifier(s);
-        if (lastState==1) return "";
-        if (bsxWindow!=null) bsxWindow.removeObject(strIdentifier);
-        else warn("no BSX Output available");
-        resetMachine();
-      }
-    // ReQuest Version
-    else if (lastCommand.equals("@RQV"))
-      {
-        if (parent!=null) parent.writeToSocket("#VER "+clientVersion+"\r\n");
-        else warn("no Socket to send to");
-        resetMachine();
-      }
-    // PROmotion
-    else if (lastCommand.equals("@PRO"))
-      {
-        if (parent!=null)
-          parent.writeToUser(
-"****************************************************************************\r\n"+
-"*                                                                          *\r\n"+
-"*  Regenbogen BSX - Ein MultiUser Dungeon mit Grafikunterst�tzung          *\r\n"+
-"*  Adresse: rb.mud.de Port: 4711 Admins: mud@rb.mud.de                     *\r\n"+
-"*  Hier ist was los Leute! Kommt. Lest. Schaut. Spielt. Und Redet.         *\r\n"+
-"*                                                                          *\r\n"+
-"* Client: von Foobar basierend auf Arbeiten von Riwa und Hate@Morgengrauen *\r\n"+
-"*                                                                          *\r\n"+
-"*****************************************************************Java*rulez*\r\n"
-                            );
-        else warn("no User to display promotion available");
-        resetMachine();
-      }
-    // display SCEne
-    else if (lastCommand.equals("@SCE"))
-      {
-        s=readIdentifier(s);
-        if (lastState==1) return "";
-        if (bsxWindow!=null)
-          if (!bsxWindow.showScene(strIdentifier))
-            if (parent!=null)
-              parent.writeToSocket("#RQS "+strIdentifier+"\r\n");
-            else warn("no Socket to send to");
-          else
-            if (debug>0)
-              System.out.println("Query BSX-Scene for "+strIdentifier);
-            else ;
-        else warn("no BSX Output available");
-        resetMachine();
-      }
-    // DeFine Object
-    else if (lastCommand.equals("@DFO"))
-      {
-        s=readIdentifier(s);
-        if (lastState==1) return "";
-        s=readBSXData(s);
-        if (lastState==2) return "";
-        BSXInputStream bis=new BSXInputStream(new StringBufferInputStream(BSXData));
-        if (bsxWindow!=null)
-          try {
-            bsxWindow.addObject(strIdentifier,bis.readBSXGraphic());
-          } catch (IOException e) { e.printStackTrace(); resetMachine(); return s; }
-        else warn("no BSX Output available");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@DFS"))
-      {
-        s=readIdentifier(s);
-        if (lastState==1) return "";
-        s=readBSXData(s);
-        if (lastState==2) return "";
-        BSXInputStream bis=new BSXInputStream(new StringBufferInputStream(BSXData));
-        if (bsxWindow!=null)
-          try {
-            bsxWindow.addScene(strIdentifier,bis.readBSXGraphic());
-          } catch (IOException e) { e.printStackTrace(); resetMachine(); return s; }
-        else warn("no BSX Output available");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@PUR"))
-      {
-        warn("@PUR not implemented yet");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@TXT"))
-      {
-        warn("@TXT not implemented (and no plan for doing that)");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@BOM"))
-      {
-        warn("@BOM not implemented (and no plan for doing that)");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@EOM"))
-      {
-        warn("@EOM not implemented (and no plan for doing that)");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@LON"))
-      {
-        warn("@LON not implemented yet");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@LOF"))
-      {
-        warn("@LOF not implemented yet");
-        resetMachine();
-      }
-    else if (lastCommand.equals("@TMS"))
-      {
-        if (parent!=null)
-        {
-          parent.writeToUser("Received @TMS to end this session.\r\n");
-        }
-        else
-          warn("@TMS received! Session not terminated. Close connection manually.");
-        resetMachine();
-      }
-    s=lastCommand+s;
-    resetMachine();
-    return s;
-  }
-}
-
-
-
diff --git a/web/root/telnet/modules/ButtonBar.class b/web/root/telnet/modules/ButtonBar.class
deleted file mode 100644
index 62a49c9dff4c6bfb913dc57fa73f98264347dbcc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 6731
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

diff --git a/web/root/telnet/modules/ButtonBar.java b/web/root/telnet/modules/ButtonBar.java
deleted file mode 100644
index 4db6b454a4..0000000000
--- a/web/root/telnet/modules/ButtonBar.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/**
- * ButtonBar -- a programmable button bar
- * --
- * $Id: ButtonBar.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Mon Aug  4 14:12:21 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package modules;
-
-import telnet;
-import frame;
-
-import java.applet.Applet;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import java.awt.Panel;
-import java.awt.Button;
-import java.awt.TextField;
-import java.awt.Event;
-import java.awt.BorderLayout;
-import java.awt.GridLayout;
-import java.awt.Dimension;
-import java.awt.Container;
-import java.awt.Frame;
-
-/**
- * This class implements a programmable button bar.
- * You can add <A HREF="#buttons">Buttons</A> and <A HREF="#fields">Input 
- * fields</A> to trigger actions in the <A HREF="telnet.html">telnet 
- * applet</A>. On how to load a module, please refer to the 
- * <A HREF="telnet.html">telnet</A> documentation.
- * 
- * <DL>
- *  <A NAME="buttons"></A>
- *  <DT><B>Buttons:</B>
- *  <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Button VALUE=&quot;<B><I>buttontext</I></B>|<B><I>buttonaction</I></B>&quot;&gt;</TT>
- *  <DD><B><I>number</I></B> is the sequence number and determines the place
- *      of the button on the row.
- *      <P>
- *  <DD><B><I>buttontext</I></B> is a string displayed on the button.
- *      <P>
- *  <DD><A NAME="buttonaction"><B><I>buttonaction</I></B></A> may be one
- *      of the following functions or strings<BR>
- *      <FONT SIZE=-1>(<I>Note:</I> the backslash character
- *      in front of the dollar sign is mandatory!)</FONT>
- *      <UL>
- *       <LI><TT><I>simple text</I></TT>
- *           to be sent to the remote host. Newline and/or carriage return
- *           characters may be added in C syntax <B>\n</B> and <B>\r</B>.
- *           To support unimplemented function keys the <B>\e</B> escape
- *           character may be useful. The <B>\b</B> backspace character is
- *           also supported.
- *           The text may contain <A HREF="#fieldreference"><B><I>field 
- *           reference(s)</I></B></A>.
- *           <P>
- *       <LI><TT>\$connect(<B><I>host</I></B>[,<B><I>port</I></B>])</TT> 
- *           tries to initiate a connection to the <B><I>host</I></B>
- *           at the <B><I>port</I></B>, if given. The standard port is
- *           23. <B><I>host</I></B> and <B><I>port</I></B> may be hostname
- *           and number or <A HREF="#fieldreference"><B><I>field
- *           reference(s)</I></B></A>. If a connection already exists
- *           nothing will happen.<BR>
- *           <FONT SIZE=-1>(<I>Note:</I> It is not allowed to have
- *           spaces anywhere inside the parenthesis!)</FONT>
- *           <P>
- *       <LI><TT>\$disconnect()</TT>
- *           terminates the current connection, but if there was no
- *           connection nothing will happen.
- *           <P>
- *       <LI><TT>\$detach()</TT>
- *           detaches the applet from the web browser window and
- *           creates a new frame externally. This may be used to allow
- *           users to use the applet while browsing the web with the
- *           same browser window.<BR>
- *           <FONT SIZE=-1>(<I>Note:</I> You need to load the applet via the
- *           <A HREF="appWrapper.html">appWrapper class</A> or
- *           it will not work properly!)</FONT>
- *      </UL>
- *  <DD><B>Examples:</B><BR>
- *      <FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
- *      examples for input fields below.)</FONT>
- *      <PRE>
- *        &lt;PARAM NAME=1#Button VALUE="HELP!|help\r\n"&gt;
- *        &lt;PARAM NAME=2#Button VALUE="HELP:|help \@help@\r\n"&gt;
- *        &lt;PARAM NAME=4#Button VALUE="simple|\$connect(localhost)"&gt;
- *        &lt;PARAM NAME=5#Button VALUE="complete|\$connect(www,4711)"&gt;
- *        &lt;PARAM NAME=6#Button VALUE="connect|\$connect(\@address@)"&gt;
- *        &lt;PARAM NAME=8#Button VALUE="connect to port|\$connect(\@address@,\@port@)"&gt;
- *        &lt;PARAM NAME=10#Button VALUE="window|\$detach()"&gt;
- *     </PRE>
- *     <P>
- *  <A NAME="fields"></A>
- *  <DT><B>Input fields</B>
- *  <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Input VALUE=&quot;<B><I>fieldname</I></B>[#<I><B>length</B></I>]|<B><I>initial text</I></B>[|<B><I>action</I></B>]&quot;&gt;</TT>
- *  <DD><B><I>number</I></B> is the sequence number and determines the place
- *      of the field on the row.
- *      <P>
- *  <DD><A NAME="fieldreference"><B><I>fieldname</I></B></A> is a
- *      symbolic name to reference the input field. A reference may be used in 
- *      <A HREF="#buttonaction"><B><I>button actions</I></B></A> and
- *      is constructed as follows:
- *      <TT>\@<B><I>fieldname</I></B>@</TT>
- *      The <B>\@fieldname@</B> macro will be replaced by the string entered in
- *      the text field.
- *      <P>
- *  <DD><B><I>length</I></B> is the length of the input field in numbers of
- *      characters.
- *      <P>
- *  <DD><B><I>initial text</I></B> is the text to be placed into the input
- *      field on startup
- *  <DD><B><I>action</I></B> may be used similar to a 
- *      <A HREF="#buttonaction"><B><I>button action</I></B></A>. This action 
- *      will be used if the users presses Return in the inputfield. Leave
- *      empty if you only want to use a button to send the text!
- *  <DD><B>Examples:</B><BR>
- *      <FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
- *      examples for buttons before.)</FONT>
- *      <PRE>
- *        &lt;PARAM NAME=3#Input VALUE="help#10|"&gt;
- *        &lt;PARAM NAME=7#Input VALUE="address|www.first.gmd.de"&gt;
- *        &lt;PARAM NAME=8#Input VALUE="send#5|who|\@send@\r\n"&gt;
- *        &lt;PARAM NAME=9#Input VALUE="port#5|4711"&gt;
- *      </PRE>
- *      <P>
- * </DL>
- * @version $Id: ButtonBar.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- * @see modules.Module
- */
-public class ButtonBar extends Panel implements Module
-{
-  // our parent is the telnet app
-  private telnet parent;
-  
-  // these tables contain our buttons and fields.
-  private Hashtable buttons = null;
-  private Hashtable fields = null;
-
-  // the top level (for detaching)
-  private Container toplevel;
-
-  /**
-   * This method is called by our loader to notify us of it. 
-   * @param o The object that has loaded this object.
-   * @see display.Module
-   */
-  public void setLoader(Object o) { parent = (telnet)o; }
-
-  /**
-   * If the applet connects this method is called.
-   * @param host remote hostaddress - not used
-   * @param port remote port - not used
-   */
-  public void connect(String host, int port) {
-    // do nothing yet.
-  }
-
-  /**
-   * Get notified of disconnection. Do nothing.
-   */
-  public void disconnect() {
-    // do nothing yet
-  }
-
-  /**
-   * This module does not take any input. It works passive.
-   * @return null to remove from the list of receiver modules.
-   * @see display.Module
-   */
-  public String receive(String s) { return null; }
-	
-  /**
-   * create the buttonbar from the parameter list. We will know our parent,
-   * when we have been added.
-   */
-  public void addNotify() {
-    if(buttons == null && fields == null) {
-      String tmp; 
-
-      int nr = 1;
-      String button = null, input = null;
-      while((button = parent.getParameter(nr+"#Button")) != null ||
-	    (input = parent.getParameter(nr+"#Input")) != null) {
-	nr++;
-	if(button != null) {
-	  if(buttons == null) buttons = new Hashtable();
-	  int idx = button.indexOf('|');
-	  if(button.length() == 0)
-	    System.out.println("ButtonBar: Button: no definition");
-	  if(idx < 0 || idx == 0) {
-	    System.out.println("ButtonBar: Button: empty name \""+button+"\"");
-	    continue;
-	  }
-	  if(idx == button.length() - 1) {
-	    System.out.println("ButtonBar: Button: empty command \""+button+"\"");
-	    continue;
-	  }
-	  Button b = new Button(button.substring(0, idx));
-	  buttons.put(b, button.substring(idx+1, button.length()));
-	  add(b);
-	} else
-	  if(input != null) {
-	    if(fields == null) fields = new Hashtable();
-	    if(buttons == null) buttons = new Hashtable();
-	    int idx = input.indexOf('|');
-	    if(input.length() == 0)
-	      System.out.println("ButtonBar: Input field: no definition");
-	    if(idx < 0 || idx == 0) {
-	      System.out.println("ButtonBar: Input field: empty name \""+input+"\"");
-	      continue;
-	    }
-	    int si, size;
-	    if((si = input.indexOf('#', 0)) == 0) {
-	      System.out.println("ButtonBar: Input field: empty name");
-	      continue;
-	    }
-	    if(si < 0 || si == idx-1) size = 10;
-	    else size = Integer.parseInt(input.substring(si+1, idx));
-	    TextField t = 
-	      new TextField(input.substring(idx + 1, 
-					    input.lastIndexOf('|') == idx ?
-					    input.length() : 
-					    (idx = input.lastIndexOf('|'))),
-			    size);
-	    buttons.put(t, input.substring(idx + 1, input.length()));
-	    fields.put(input.substring(0, (si < 0 ? idx : si)), t);
-	    add(t);
-	  }
-	button = input = null;
-      }
-    }
-    super.addNotify();
-  }
-
-  public boolean handleEvent(Event evt) {
-    String tmp;
-    if(evt.id == Event.ACTION_EVENT &&
-       (tmp = (String)buttons.get(evt.target)) != null) {
-      System.out.println("ButtonBar: "+tmp);
-      String cmd = "", function = null;
-      int idx = 0, oldidx = 0;
-      while((idx = tmp.indexOf('\\', oldidx)) >= 0 && 
-	    ++idx <= tmp.length()) {
-	cmd += tmp.substring(oldidx, idx-1);
-	switch(tmp.charAt(idx)) {
-	case 'b': cmd += "\b"; break;
-	case 'e': cmd += ""; break;
-	case 'n': cmd += "\n"; break;
-	case 'r': cmd += "\r"; break;
-	case '$': {
-	  int ni = tmp.indexOf('(', idx+1);
-	  if(ni < idx) {
-	    System.out.println("ERROR: Function: missing '('");
-	    break;
-	  }
-	  if(ni == ++idx) {
-	    System.out.println("ERROR: Function: missing name");
-	    break;
-	  }
-	  function = tmp.substring(idx, ni);
-	  idx = ni+1;
-	  ni = tmp.indexOf(')', idx);
-	  if(ni < idx) {
-	    System.out.println("ERROR: Function: missing ')'");
-	    break;
-	  }
-	  tmp = tmp.substring(idx, ni);
-	  idx = oldidx = 0;
-	  continue;
-	}
-	case '@': {
-	  int ni = tmp.indexOf('@', idx+1);
-	  if(ni < idx) {
-	    System.out.println("ERROR: Input Field: '@'-End Marker not found");
-	    break;
-	  }
-	  if(ni == ++idx) {
-	    System.out.println("ERROR: Input Field: no name specified");
-	    break;
-	  }
-	  String name = tmp.substring(idx, ni);
-	  idx = ni;
-	  TextField t;
-	  if(fields == null || (t = (TextField)fields.get(name)) == null) {
-	    System.out.println("ERROR: Input Field: requested input \""+
-			       name+"\" does not exist");
-	    break;
-	  }
-	  cmd += t.getText();
-	  t.setText("");
-	  break;
-	}
-	default : cmd += tmp.substring(idx, ++idx);
-	}
-	oldidx = ++idx;
-      }
-
-      if(oldidx <= tmp.length()) cmd += tmp.substring(oldidx, tmp.length());
-      
-      if(function != null) {
-	if(function.equals("exit")) { 
-	  try {
-	    System.exit(0);
-	  } catch(Exception e) { e.printStackTrace(); }
-	}
-	if(function.equals("connect")) {
-	  String address = null;
-	  int port = -1;
-	  try {
-	    if((idx = cmd.indexOf(",")) >= 0) {
-	      try {
-		port = Integer.parseInt(cmd.substring(idx+1, cmd.length()));
-	      } catch(Exception e) {
-		port = -1;
-	      }
-	      cmd = cmd.substring(0, idx);
-	    }
-	    if(cmd.length() > 0) address = cmd;
-	    if(address != null) 
-	      if(port != -1) parent.connect(address, port);
-	      else parent.connect(address);
-	    else parent.connect();
-	  } catch(Exception e) {
-	    System.err.println("ButtonBar: connect(): failed");
-	    e.printStackTrace();
-	  }
-	} else
-	  if(function.equals("disconnect") && parent.disconnect())
-	    parent.send("\r\nClosed connection.\r\n");
-	  else
-	    if(function.equals("detach")) {
-	      if(parent.getParent() instanceof Frame) {
-		Frame top = (Frame)parent.getParent();
-		if(toplevel != null) {
-		  System.out.println("ButtonBar: reattaching applet...");
-		  toplevel.setLayout(new BorderLayout());
-		  toplevel.add("Center", parent);
-		  toplevel.validate();
-		  toplevel.layout();
-		  toplevel = null;
-		} else {
-		  System.out.println("ButtonBar: destroying window...");
-		  parent.disconnect();
-		}
-		top.dispose();
-	      } else {
-		System.out.println("ButtonBar: detaching applet...");
-		toplevel = parent.getParent();
-		frame top = new frame("The Java Telnet Applet");
-		Dimension s = parent.size();
-		top.reshape(0, 0, s.width, s.height);
-		top.setLayout(new BorderLayout());
-		top.add("Center", parent);
-		top.pack();
-		top.show();
-	      }
-	    }
-	    else
-	      System.out.println("ERROR: function not implemented: \""+
-				 function+"\"");
-	return true;
-      }
-      // cmd += tmp.substring(oldidx, tmp.length());
-      if(cmd.length() > 0) parent.send(cmd);
-      return true;
-    }
-    return false;
-  }
-}
-
diff --git a/web/root/telnet/modules/Module.class b/web/root/telnet/modules/Module.class
deleted file mode 100644
index de74206f80ea43457fdd6d25c0b722cae3572861..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 419
zcmX^0Z`VEsW(HjbX?6x-b_P*K24)S-Fh&MZ4WF#UvPAuy#JqHU|D>$c<PvL;xEiu}
za7j^SUb?j=l2WJ;BZC+gg`OZ2Se)}yQW+T-JsBDJeKPY>{YrC_Qj0<olX6lS8HA9G
zg4&8I2sdBACpSN(G$*xK-xo|<gVg0GC+37D7G;7AgPO<<k=6tGhLM3QIKQ+gIn^x_
zWEp#MeqLT`atR{?S4w6voWqilUtGe-AOO;V?0-fEww(OLl++?d20m1~85vj#@{39s
z8Q6<blT$OxK;A26WZ*1LE%AZsU@6JSEM{Y5U}9io009;jMg{=}HU<V3Mg~C!W-u+z
WzzU`%8CVz?7#JD&85kIt82A7hP;I*a

diff --git a/web/root/telnet/modules/Module.java b/web/root/telnet/modules/Module.java
deleted file mode 100644
index 422f84f5ad..0000000000
--- a/web/root/telnet/modules/Module.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Module -- Module interface
- * --
- * $Id: Module.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Mon Mar 24 15:35:13 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package modules;
-
-/**
- * Modules must implement this interface to be detected as valid modules
- * @version $Id: Module.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- */
-public interface Module 
-{
-	/**
-	 * Set the loader of the module. This is necessary to know if you want to
-	 * contact the modules parent.
-	 * @param loader The object that has loaded this module.
-	 */
-	public void setLoader(Object loader);
-
-	/**
-	 * Connected to the remote host. This method notifies upon new connection.
-	 * @param host remote hostname
-	 * @param port remote port
-	 */
-	public void connect(String host, int port);
-	
-	/**
-	 * Disconnect from the host. This method notifies of lost connection.
-	 */
-	public void disconnect();
-	
-	/**
-	 * Receive data from somewhere. If a modules does not want to receive data
-	 * it should return null to remove itself from the list of receiver modules.
-	 * @param s The string we receive.
-   * @return the modified string or null (to remove from receiver list)
-	 */
-	public String receive(String s);
-}
-
diff --git a/web/root/telnet/modules/MudConnector.class b/web/root/telnet/modules/MudConnector.class
deleted file mode 100644
index 646fe276964ffc12fd4f25dc22e285bb3af3b495..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 7696
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

diff --git a/web/root/telnet/modules/MudConnector.java b/web/root/telnet/modules/MudConnector.java
deleted file mode 100644
index 3b6039bccd..0000000000
--- a/web/root/telnet/modules/MudConnector.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/**
- * MudConnector -- This module is especially designed for the 
- *                 MUD Connector (http://www.mudconnect.com/)
- *                 It loads a tabulator separated list via http and
- *                 displays it in a List-Box.
- * --
- * $Id: MudConnector.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Sun Apr 13 22:29:16 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-package modules;
-
-import telnet;
-
-import java.applet.*;
-import java.awt.*;
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-/**
- * A specially designed module for the 
- * <A HREF="http://www.mudconnect.com/">MUD Connector</A>.
- * It loads a list of MUDs via http and displays them in a list box to 
- * be selected. A selected MUD can then be entered.
- *
- * <DL>
- *  <DT><B>MudConnector parameterfile:</B>
- *   <DD><PRE>&lt;PARAM NAME=MUDlist VALUE=&quot;<B><I>url</I></B>&quot;&gt;</PRE>
- *   <DD>The url of the Mudlist. This url MUST be located on the same web
- *       server as the applet!
- * </DL>
- * @version $Id: MudConnector.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- * @see modules.Module
- */
-public class MudConnector extends Panel implements Module, Runnable
-{
-  // parent applet
-  private telnet app;
-
-  // server url for the mudlist
-  private String url;
-  private URL server;
-
-  // vector containing the mud data (host#port)
-  private Vector index = new Vector();
-  // the actually displayed list of muds
-  private java.awt.List display = new java.awt.List();
-
-  private CardLayout layouter;
-  private Panel progress, address;
-  private Label indicator;
-  private TextField info;
-
-  // buttons
-  private Button connectButton, disconnectButton, refreshButton, showButton;
-
-  /**
-   * setLoader() is called upon start of the parent applet. This method
-   * initializes the GUI of the module, e.g the list and buttons.
-   * @param loader the parent applet
-   */
-  public void setLoader(Object loader) {
-    // do nothing if we already know our loader
-    if(app != null) return;
-    app = (telnet)loader;
-
-    // cardlayout to display either the progress while loading the mudlist
-    // or the mud listbox plus its buttons.
-    setLayout(layouter = new CardLayout());
-
-    add("PROGRESS", progress = new Panel());
-    progress.add(indicator = new Label("Loading mudlist, please wait..."));
-    
-    // the interactive module GUI is arranged in a grid
-    GridBagLayout grid = new GridBagLayout();
-
-    address = new Panel();
-    address.setLayout(grid);
-    
-    // the constraints for each of the components
-    GridBagConstraints constraints = new GridBagConstraints();
-
-    // the listbox is arranged on the left side getting most of the space
-    constraints.gridheight = 2;
-    constraints.weightx = 2.0;
-    constraints.fill = GridBagConstraints.BOTH;
-    grid.setConstraints(display, constraints);
-    address.add(display);
-
-    // generic panel that places its components centered.
-    Panel panel = new Panel();
-
-    // the button panel, buttons are placed east of the list and on the top
-    panel.add(showButton = new Button("Info!"));
-    panel.add(connectButton = new Button("connect"));
-    panel.add(disconnectButton = new Button("disconnect"));
-    panel.add(refreshButton = new Button("refresh list"));
-    constraints.weightx = 0.0;
-    constraints.weightx = 0.0;
-    constraints.gridheight = 1;
-    constraints.gridwidth = GridBagConstraints.REMAINDER;
-    constraints.fill = GridBagConstraints.NONE;
-    grid.setConstraints(panel, constraints);
-    address.add(panel);
-
-    // the mud information text is below the buttons
-    (panel = new Panel()).add(info = new TextField(30));
-    info.setEditable(false);
-    address.add(panel);
-    grid.setConstraints(panel, constraints);
-    add("ADDRESS", address);
-
-    layouter.show(this, "PROGRESS");
-  }
-  
-  /**
-   * when newly added try to load the mudlist using the parameter "mudlist"
-   */
-  public void addNotify()
-  {
-    super.addNotify();
-    if(url == null) {
-      if(app.getParameter("mudlist") != null) {
-        url = app.getParameter("mudlist");
-        loadData();
-      }
-      else {
-        indicator.setText("The \"mudlist\" is not set, "+
-                          "cannot load data!");
-        System.out.println("MudConnector: cannot load data, missing parameter");
-      }
-    }
-  }
-
-  /**
-   * initiate the loading process, display progress meter
-   */
-  private void loadData() 
-  {
-    layouter.show(this, "PROGRESS");
-    
-    Thread t = new Thread(this);
-    t.setPriority(Thread.MIN_PRIORITY);
-    t.start();
-  }
-
-  /**
-   * The body of the thread opens a URLConnection with the address given as
-   * parameter "mudlist" and downloads it. It expects a tabulator separated
-   * list <mudname> <mudhost> <mudport> and the amount of muds in the file
-   * at the beginning of the file.
-   */
-  public void run() 
-  {
-    try {
-      System.out.print("MudConnector: loading data...");
-      if(display.countItems() > 0) display.clear();
-      index = new Vector();
-      
-      // open the url and the data stream
-      server = new URL(url);
-      StreamTokenizer ts = new StreamTokenizer(server.openStream());
-      // reset the syntax and make all but the chars from 0-31 to
-      // plain text
-      ts.resetSyntax();
-      ts.whitespaceChars(0, 31);
-      ts.wordChars(32, 255);
-
-      // paint a rectangle for the progress indicator with the size of the
-      // text above
-      Graphics pg = progress.getGraphics();
-
-      // initialize the current number of muds loaded and load the maximum
-      // number of muds in the file (first line in the file)
-      int p = 1, max = 1;
-      int token = ts.nextToken();
-      if(token != ts.TT_EOF) try {
-        ts.sval = ts.sval.substring(1);
-        max = Integer.parseInt(ts.sval); 
-        System.out.print("["+max+" muds expected] ");
-        token = ts.nextToken();
-      } catch(NumberFormatException e) {
-        System.out.print("'"+ts.sval+"'");
-        System.out.print("[# of muds incorrect, expecting 1000] ");
-        max = 1000;
-      }
-      
-      // read line by line and display the progress bar
-      while(token != ts.TT_EOF) {
-        pg.setColor(getBackground());
-        pg.draw3DRect(indicator.location().x - 1, 
-                      indicator.location().y + indicator.size().height + 4, 
-                      indicator.size().width + 1, 21, false);
-        pg.fill3DRect(indicator.location().x, 
-                    indicator.location().y + indicator.size().height + 5, 
-                    p++ * indicator.size().width / max, 20, true);
-        
-        String name = ts.sval; token = ts.nextToken();
-        if(token != ts.TT_EOL && token != ts.TT_EOF) {
-          String host = ts.sval; token = ts.nextToken();
-          int port;
-          try {
-            port = Integer.parseInt(ts.sval);
-          } catch(NumberFormatException e) {
-            port = 23;
-          }
-          display.addItem(name);
-          index.addElement(host+"#"+port);
-        } else 
-          System.out.println("unexpected ("+name+") "+
-                             (token == ts.TT_EOF ? "EOF" : "EOL"));
-        while((token = ts.nextToken()) != ts.TT_WORD && 
-              token != ts.TT_EOF);
-      }
-    } catch(Exception e) {
-      indicator.setText("The \"mudlist\" parameter is incorrect, "+
-                        "cannot load data!");
-      e.printStackTrace();
-    }
-    System.out.println("("+index.size()+" muds)...done");
-    layouter.show(this, "ADDRESS");
-  }
-  
-  /**
-   * handle list selection, connect, disconnect and refresh button
-   * @param evt the event to process
-   */
-  public boolean handleEvent(Event evt) {
-    if(evt.target == connectButton && evt.id == Event.ACTION_EVENT) {
-      if(display.getSelectedIndex() < 0 ||
-         display.getSelectedIndex() > index.size()) {
-        info.setText("You did not select a MUD!");
-        return false;
-      }
-      String address = (String)index.elementAt(display.getSelectedIndex());
-      int port = Integer.parseInt(address.substring(address.indexOf('#')+1));
-      app.connect(address.substring(0, address.indexOf('#')), port);
-      return true;
-    }
-    if(evt.target == disconnectButton && evt.id == Event.ACTION_EVENT)
-      app.disconnect();
-    if(evt.target == refreshButton && evt.id == Event.ACTION_EVENT)
-      loadData();
-    if(evt.target == showButton && evt.id == Event.ACTION_EVENT) {
-      if(display.getSelectedIndex() < 0 ||
-         display.getSelectedIndex() > index.size()) {
-        info.setText("You did not select a MUD!");
-        return false;
-      }
-      URL page = null;
-      try {
-        page = new URL("http://www.mudconnect.com/mud-bin/adv_search.cgi"+
-                       "?Mode=MUD&mud="+
-                       display.getSelectedItem().replace(' ', '+'));
-      } catch(MalformedURLException e) {
-        info.setText("There was an URL error!");
-        e.printStackTrace();
-        return false;
-      }
-      app.getAppletContext().showDocument(page, "_TOP");
-      return true;
-    }
-    
-    if(evt.target == display && evt.id == Event.LIST_SELECT) {
-      String tmp = (String)index.elementAt(display.getSelectedIndex());
-      info.setText(tmp.substring(0, tmp.indexOf('#'))+" "+
-                   tmp.substring(tmp.indexOf('#')+1));
-    }
-    return false;
-  }
-
-  /**
-   * dummy methods
-   */
-  public void connect(String host, int port) {}
-  public void disconnect() {}
-  public String receive(String str)
-  {
-    return null;
-  }
-  
-}
diff --git a/web/root/telnet/modules/Script.class b/web/root/telnet/modules/Script.class
deleted file mode 100644
index 35b5c7f2735e7113003c4287d7632b8f8c3ce679..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2809
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

diff --git a/web/root/telnet/modules/Script.java b/web/root/telnet/modules/Script.java
deleted file mode 100644
index 8eb47842ee..0000000000
--- a/web/root/telnet/modules/Script.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/**
- * Script -- A module for scripting (very simple).
- * --
- * $Id: Script.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Mon Mar 24 15:52:12 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package modules;
-
-import telnet;
-import java.util.Hashtable;
-import java.util.Enumeration;
-import java.awt.Dialog;
-import java.awt.Frame;
-import java.awt.TextField;
-import java.awt.Label;
-import java.awt.Event;
-
-/**
- * A very simple scripting module. It takes pairs of pattern and text and
- * sends the corresponding text when the pattern matches. Each pattern is
- * only matched once per connected session.
- *
- * <DL>
- *  <DT><B>Scripts:</B>
- *   <DD><PRE>&lt;PARAM NAME=script VALUE=&quot;<B><I>pattern</I></B>|<B><I>text</I></B>|<B><I>...</I></B>&quot;&gt;</PRE>
- *   <DD>A script contains of pairs of <I>pattern</I> and <I>text</I> strings.
- *       If the pattern is matched against the output from the remote host,
- *       the corresponding text will be sent. Each pattern will match only
- *       <B>once</B> per session. A session is defined by connect and 
- *       disconnect.<P>
- *       Thus it is possible to program an autologin as follows:<BR>
- *       <PRE><B>"login:|leo|Password:|mypassword|leo@www|ls"</B></PRE>
- *       Newlines will be added automatically to the string sent! At the
- *       moment the order of the pattern and text pairs is <I>not</I> relevant.
- *       <P>
- *       It is possible to prompt the user for input if a match occurs. If the
- *       corresponding <I>text</I> is a string enclosed in braces ([] or {}) a
- *       dialog window is opened with <I>text</I> as prompt. A special case 
- *       is an empty prompt in which case the <I>pattern</I> will be shown as 
- *       prompt. &quot;[Your name:]&quot; would open a dialog window with the
- *       text &quot;Your name&quot; as prompt. Curly braces have a special
- *       meaning; any user input will be shown as &quot;*&quot; which makes
- *       it possible to program password prompts. Example: 
- *       &quot;{Your password:}&quot;.<P>
- *       A special match like: &quot;login:|[]&quot; can be used to open a
- *       dialog and display &quot;login:&quot; as prompt. This works for
- *       &quot;{}&quot; as well.
- *       
- * </DL>
- * @version $Id: Script.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- * @see modules.Module
- */
-public class Script extends Hashtable implements Module
-{
-  // This is the target for any text we want to send
-  private telnet applet = null;
-
-  /**
-   * Set the applet as module loader
-   * @param o the object that is the applet (must be an Applet)
-   * @see module.Module
-   * @see java.applet.Applet
-   */
-  public void setLoader(Object o) { applet = (telnet)o; }
-	
-  /**
-   * Configure the script module by reading the script PARAMeter.
-   * @param host remote hostaddress - not used
-   * @param port remote port - not used
-   */
-  public void connect(String host, int port) {
-    String tmp = applet.getParameter("script");
-    
-    // delete all entries
-    clear();
-    
-    if(tmp != null) {
-      int idx = tmp.indexOf('|');
-      int oldidx = 0;
-      while(idx >= 0) {
-	String match = tmp.substring(oldidx, idx);
-	oldidx = idx;
-	idx = tmp.indexOf('|', idx+1);
-	idx = idx < 0 ? idx = tmp.length() : idx;
-	String send = tmp.substring(oldidx+1, idx);
-	put(match, send);
-	oldidx = idx+1;
-	idx = tmp.indexOf('|', idx+1);
-      }
-    }
-  }
-
-  /**
-   * Get notified of disconnection. Do nothing.
-   */
-  public void disconnect() {}
-	
-  /**
-   * This method is called when data is received. It tries to match the
-   * input to the list of patterns and sends corresponding text on success.
-   * If the response is [] or {} the user will be prompted with the matching
-   * text. You can modify the prompt string by entering it inside of the
-   * brackets or curly braces (e.g. [Enter your id:]). In case of curly
-   * braces the input area will not show the typed in text (for passwords)!
-   *
-   * @param s The string to test.
-   * @see peer.InputPeer
-   */
-  public String receive(String s) {
-    if(isEmpty()) return s;
-    Enumeration match = keys();
-    while(match.hasMoreElements()) {
-      String key = (String)match.nextElement();
-      if(s.indexOf(key) != -1) {
-	String value = (String)get(key);
-	if(value.indexOf("[") == 0 || value.indexOf("{") == 0) {
-	  TextField input = new TextField(20);
-	  if(value.startsWith("{")) input.setEchoCharacter('*');
-	  if("[]".equals(value) || "{}".equals(value)) value = key;
-	  else value = value.substring(1, value.length() - 1);
-	  Thread current = Thread.currentThread();
-	  new UserDialog(new Frame(), value, false, current, input);
-	  current.suspend();
-	  value = input.getText();
-	}
-	applet.send(value + "\r");
-	remove(key);
-      }
-    }
-    return s;
-  }
-}
-
-class UserDialog extends Dialog {
-  TextField input;
-  Thread thread;
-  String value;
-
-  public UserDialog(Frame parent, String value, boolean modal, 
-		    Thread t, TextField reply) {
-    super(parent, value, modal);
-    thread = t; input = reply;
-    add("West", new Label(value));
-    add("Center", input);
-    pack();
-    show();
-  }
-
-  public boolean handleEvent(Event evt) {
-    if(evt.target == input && evt.key == 10) {
-      thread.resume();
-      hide(); dispose();
-      return true;
-    }
-    return false;
-  }
-}
diff --git a/web/root/telnet/modules/TextLabel.class b/web/root/telnet/modules/TextLabel.class
deleted file mode 100644
index 29c98729da91621470ed25a962bc43a73a9227d8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1975
zcmX^0Z`VEsW(Hk`QVs@N4u&`mhIkH!1P+Eoc7{-PhA?)9aCU|Wc7{lHhA4K1Xm*Ae
zc7|AXh9q`|WOjxEE(UG}MJ@(T1|=>AE(T>T20jqM!Jxv$z|Now;;L~n2r#H~F$gkf
za53;QXmT;|FlccxurX+Zl<0uObV2I$co^&$3_yf2h_C_?Hara83?@7bJ`6@Y489B=
zJPd&hmOKn03>G{LX&@q-!IXy~hrx`8A)mpJhoOW)pOHaCL(?ZKu`E$PCowNwKe(hQ
zGcVnmk%3u5GmMczRKpXitTZ-pr_!{v)FNv}238GEPmpdc4T#B!<t6&g`MCx8d8v6N
z)=&dc1sNHHkQBS+gVcpFGDsr{`XpB7mzMY@<|U@57FmPL7lq1#UGAThm6}|FDGv3K
zrY9qV8nSwrIK+uyBcMVcZ)1vrEEL0H0NAJE*d)L*j5>@AY&MyBnI(3N3@pz1DXEMM
zjGl}P{63j^seYxoNvTC4iAg!BAQ5mhfWigre?|rYh$}!MK8Z=GIo6B}LJ%RabD=&)
z6@>di*e5qXr8FnCSU)7Sq6DEoKRGccEU_pPWGU2U4xf_LoV?T$YeojH;QZ2}<W#pz
zkc;@>I`u$KV`N}XOi5v6U`s40NX>&Xa#Bke8Ca6@bBY-m*pu_~@=}va7#X-yGK=9H
zp7hj`fW)H2+|-iPB1Q(5jQrvfMh520lnO=$_RPGL)C&JJMg}2}75X5*!jlTBU|vaL
zW**1@Ubsq7I59E^!};z-nJJKD!pOh}7X|wrSs)-WFExjeK@hA09F(4UC8_Bkvjq@h
zkdS0#5JMM&q;p0Fwj5BTC^Ir}f*H=>Fo3Wj4ui0R^2>`E8Teqa;R}uxMg~Ec06dTx
z8JP0&85tP!85uYV5{rscJ@Y_eTaaH=!pOj0l$xBHS(eJkz)}P@kP#H%#i=DwtBO-g
zU_R#rvGWsCK>lGV&a6shWZ*0=O)3UwZAJz*NMtiIu#}`$fGjA<$Sh`LU@plmU}WGZ
z$%puxL6Jd#ft!JmfuDhqfdN#SFfcH%GBAQl9R@}Qdj<vueFjFb9IMuL2BD1%3=B*R
zjtmS8Y+ykX26qMq1|Be*m7$2ije&uIk-?wA8mbv&05eDoU1L00qXGk15~>lTHHg6#
zY#1X$I)eiP69Xed0D}S4Fl~@QAdGHU3fM4+D;S}MF*0N@SVA?LF)%VPfn3hG0YoCX
zKO3x53?d8F3G!hU*cprrxeVqIGZ<$xurhEkFfg3f(muc-xRpVY#g@fRYYT&<5bHJu
zDJvF9mSYT@k}NwIWNg8r%-a~GBCX(pc3Qg`I3s0bw=)QDV~}ICVg)N=fhjVQWD#QA
z#vpCRCctOLF3QF<pFvQPMU;&tVH<<|4h9)B_T3B|k&-OTqU_rl#H?6Z%-EJQDDGz9
zij-v8&LCpNF3Ea~flHEIl64z{td=DEHU{x<kPW*T_#%avwlj!|vbC@=Zex(x#-ISP
z^Z5TGh+qt25Mf|ouxH?4aAe?NaApu^aA8nlaAh!LaAPoM@L+IZ@M3Uh@MZ{O@L`B&
z@MTD5@M9=v@Mmac2x9192xjPJ2w|AU5X!KKA&g-qLomZB20L)TrZI4Vf(#z8rx{Wi
zTo@P`xEKyHWP;<Dhhaa1AA<t}GlMw87KUO5R|Xb_WCkUMJO&2_b_REb7KQ`{Ck9rA
lcm_3wU<L;UHiixcXND9oYZ`+vLm`7R13LpFgC{s*y#O{BuHFCu

diff --git a/web/root/telnet/modules/TextLabel.java b/web/root/telnet/modules/TextLabel.java
deleted file mode 100644
index b2c6eeb623..0000000000
--- a/web/root/telnet/modules/TextLabel.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * TextLabel -- A module to display a Label on the applet.
- * --
- * $Id: TextLabel.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Wed Jul  9 17:37:28 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package modules;
-
-import telnet;
-import java.awt.Panel;
-import java.awt.Label;
-import java.awt.GridLayout;
-import java.awt.Font;
-
-/**
- * This small module lets you display text somewhere in the applets area.
- *
- * <DL>
- *  <DT><B>Label:</B>
- *   <DD><PRE>&lt;PARAM NAME=labelRows    VALUE=&quot;<B><I>rows</B></I>&quot;&gt;</PRE>
- *   <DD>Defines the how many rows the label will have.
- *   <DD><PRE>&lt;PARAM NAME=labelCols    VALUE=&quot;<B><I>cols</B></I>&quot;&gt;</PRE>
- *   <DD>Defines the how many columns the label will have.
- *   <DD><PRE>&lt;PARAM NAME=labelFont    VALUE=&quot;<B><I>font[,size]</B></I>&quot;&gt;</PRE>
- *   <DD>The font for displaying the label text. If the <I>size</I> is left out
- *       a standard size of 14 points is assumed.
- *   <DD><PRE>&lt;PARAM NAME=label#<I>number</I> VALUE=&quot;<B><I>text</I></B>&quot;&gt;</PRE>
- *   <DT>The labels are enumerated and displayed in rows and columns.
- * </DL>
- * @version $Id: TextLabel.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- * @see modules.Module
- */
-public class TextLabel extends Panel implements Module
-{
-  telnet applet;
-  
-	/**
-	 * Set the applet as module loader and configure.
-	 * @param o the object that is the applet (must be an Applet)
-	 * @see module.Module
-	 * @see java.applet.Applet
-	 */
-	public void setLoader(Object o) { 
-    applet = (telnet)o;
-
-    int rows = 1, cols = 1;
-    
-    String tmp = applet.getParameter("labelRows");
-    if(tmp != null) rows = Integer.parseInt(tmp);
-    if((tmp = applet.getParameter("labelCols")) != null)
-      cols = Integer.parseInt(tmp);
-
-    setLayout(new GridLayout(rows, cols));
-
-    Font labelFont = null;
-    if((tmp = applet.getParameter("labelFont")) != null) {
-      int idx = tmp.indexOf(",");
-      int size = 14;
-      if(idx != -1) size = Integer.parseInt(tmp.substring(idx+1));
-      labelFont = new Font(tmp, Font.PLAIN, size);
-    }
-    
-    int no = 1;
-    while((tmp = applet.getParameter("label#"+no++)) != null) {
-      Label text = new Label(tmp);
-      if(labelFont != null) text.setFont(labelFont);
-      add(text);
-    }
-  }
-	
-	/**
-	 * Do nothing upon connect.
-	 * @param host remote hostaddress - not used
-	 * @param port remote port - not used
-	 */
-	public void connect(String host, int port) {}
-  
-	/**
-	 * Do nothing upon disconnecton.
-	 */
-	public void disconnect() {}
-	
-	/**
-   * Do nothing when receiving text. Be removed upon first call.
-	 * @param s The string received.
-	 * @see peer.InputPeer
-	 */
-	public String receive(String s) { return null; }
-}
-
diff --git a/web/root/telnet/modules/UserDialog.class b/web/root/telnet/modules/UserDialog.class
deleted file mode 100644
index d75c8682b5b3d91af341aacf96770d8656564b02..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1313
zcmX^0Z`VEsW(Hk`Fb)Q74hBnh21j-VCw2yBb_N%A23K|lH+BYhb_OqY25&9~Rt5<!
z26hHXE(R6`DJ}*M25Bw^W(FBf25tsfP6jpxIgpq<7Xv4Q0!XnUNK6SNtIWfo#h?Zv
zG<X=S88mqqY#3B{80<lW1A`$Cg9n2y4}%Yg>klFVKtvFO5f4KMg9#%8vxa6EBZIJp
zPgY`CqJCm|iN0%DYF>%8W)vfX8j`46QDSbYHAGcTVqUs_a7j^SUb=M@NV^X)3X$}N
zWE7<)ra(;t*%^{rQR0@Fnv-G;GENj~9I`ziaT_e+5K}>BIOpdU<mZ9ij!lq}fz2i}
zFSEpsk%7%QHLoPKh>?NCIX@+pk%7^Zk%8YQGcVPzG&d==C?qi{CzX*w0AUIwNKk|z
z;laotf>4F<1S5kGL>TM?|D>$c<PvLCL1^%y3PQsLWNL1HN@-4Nv3_WAYLQE3VorWK
z*ns@x#GJ6iqD+tzq3+}kPA<wUDA5B&AR_};aDHh~a;jS<$O|mtsl_FX42)5X49tls
zDU1y4DVfCu`NgS>49uxzC5#N*8Hsr*IjLa(F*2}ZWP*Z&H8ZcEw1kmC2xKBSNx*{^
zRWPq4F*6Soj{I<y5T`OS@WDl(W+McA5|dJM7#R?H!ZY(y^2-?+1i*U0K?Vs3Mh52W
z)JjGM*4+G*#2iKjK3JeYe9Xuo1QS4n5+egkL1J<?BLiDOVo_>d2_pk*QEEX>B_jh{
zQEG8%ZYm=KOK}FsI>r)42DXyKqV&`fMh2FWjLc#XADl878Cc5_b4pVg6d1S}SQ!`?
zm>C!t92g84Kp=#{1Vk}}G8i*3FfcJNGH5ZVGcYpfFfcHzVqj%pV_;w~)6&+F-Oj+H
zCCRdtL0n6cbt{9QmJri!2KGpu?F_uz82G}qgc!B9FbIN~+ZcqkwlRoq1e>SFz`!8K
zz|O$Huz-P`VIhMs!y*PHhQ$oJ3`-ac8J04bGR$KzV_;yggV@9n%wP(3j~Rn6g8>60
z$ab*Xn7}Mg1}m`Z%@_h1Oc+?eVtx#U46F={40a5vP<LlDFfoACt7~sz5Cb`D8H23$
z76u^!t_2JtT3Z;zw=sxlZDSD7+Qz`ovW9sL+@WIN;91Ea!mx@#nqf7AGQ%1MRfct7
Z2Z}+Agga23!InXvfsuic!2+z`8~~{7FKqw-

diff --git a/web/root/telnet/modules/bsx/BSXDisplay.class b/web/root/telnet/modules/bsx/BSXDisplay.class
deleted file mode 100644
index 3a02a183fd15ea156f85f849aef6d6b6847ae4fe..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 5672
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

diff --git a/web/root/telnet/modules/bsx/BSXDisplay.java b/web/root/telnet/modules/bsx/BSXDisplay.java
deleted file mode 100644
index 263f4f8ad7..0000000000
--- a/web/root/telnet/modules/bsx/BSXDisplay.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * BSXDisplay 	-- a graphics component for drawing BSX Objects on
- * --
- * $Id: BSXDisplay.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Thu Feb 15 00:47:34 1996 by Matthias L. Jugel :$ 
- */
-package modules.bsx;
-
-import java.awt.*;
-import java.util.Hashtable;
-import java.util.Enumeration;
-import java.util.Vector;
-
-public class BSXDisplay extends Frame
-{
-private int Scale = 100;		/* display scale */
-  
-private Hashtable scenes;		/* scene store */
-private Hashtable objects;		/* object store */
-  
-private String curScene = "";		/* current scene */
-  
-private Image imageBuffer;
-private Graphics gBuffer;
-public BSXDisplay() { this("BSXModule - Visit Regenbogen rb.mud.de:4780"); }
-public BSXDisplay(String title) {
-  super(title);
-  addNotify();
-  imageBuffer = createImage(511,255);
-  if(imageBuffer==null) {
-    System.out.println("Couldn't create an offscreen-image :(");
-    System.exit(1);
-    }
-  gBuffer = imageBuffer.getGraphics();
-  scenes = new Hashtable(); 
-  objects = new Hashtable();
-  setScale(100);
-  resize(510,255);
-  show();
-}
-  
-public void setScale(int scale) {
-  Scale = scale;
-  resize(511 * Scale / 100, 255 * Scale / 100);
-}
-  
-  /* add a scene to the store, do not yet display */
-public void addScene(String id, BSXGraphic picture)
-  {
-    BSXScene scene = new BSXScene(picture);
-    BSXScene sc = (BSXScene)scenes.get(id);
-    if(sc != null)
-      {
-	scene.objects = sc.objects;
-	scenes.remove(id);
-      }
-    scenes.put(id, scene);
-    if(curScene.equals(id)) redraw();
-  }
-  
-  /* show current scene */
-public boolean showScene() { return showScene(curScene); }
-  
-  /* show scene given as argument */
-public boolean showScene(String id)
-  {
-    curScene = id;
-    if(!scenes.containsKey(id)) 
-      {
-	addScene(id, new BSXGraphic());
-	return false;
-      }
-    redraw();
-    return true;
-  }
-  
-  /* add object to store */
-public void addObject(String id, BSXGraphic o)
-  {
-    objects.put(id, o);
-    BSXScene scene = (BSXScene)scenes.get(curScene);
-    if(scene != null && scene.objects.containsKey(id)) redraw();
-  }
-  
-  /* show object at position 0, layer 0 */
-public boolean showObject(String id) { return showObject(id, 0, 0); }
-  
-  /* show object at position in layer */
-public boolean showObject(String id, int position, int layer)
-  {
-    BSXScene scene = (BSXScene)scenes.get(curScene);
-    
-    /* add object to scene database */
-    if((scene = (BSXScene)scenes.get(curScene)) == null) 
-      {
-	addScene(curScene, new BSXGraphic());
-	scene = (BSXScene)scenes.get(curScene);
-      }
-    
-    BSXObject o = new BSXObject(position, layer);
-    scene.objects.put(id, o);
-    
-    /* check if object is in our database */
-    if(!objects.containsKey(id)) return false;
-    
-    redraw();
-    return true;
-  }
-  
-  /* remove object from scene, if scene exists */
-public boolean removeObject(String id)
-  {
-    BSXScene scene = (BSXScene)scenes.get(curScene);
-    if(scene != null)
-      {
-	BSXObject o = (BSXObject)scene.objects.get(id);
-	scene.objects.remove(id);
-	if(o != null) 
-	  redraw();
-      }
-    return true;
-  }
-  
-  /* redraw image buffer */
-public void redraw()
-  {
-    BSXScene scene = (BSXScene)scenes.get(curScene);
-    if(scene == null) return;
-    
-    Vector[] layer = new Vector[8];
-    /* create layers */
-    for(Enumeration e = scene.objects.keys(); e.hasMoreElements();) 
-      {
-	String key; key = (String)e.nextElement();
-	BSXObject o = (BSXObject)scene.objects.get(key);
-	if(layer[o.layer] == null) layer[o.layer] = new Vector();
-	layer[o.layer].addElement(key);
-      }
-    
-    /* draw background graphic */
-    if(scene.background != null && scene.background.size() > 0)
-      drawPicture(scene.background);
-    else
-      return;
-    
-    /* display layers from back to front */
-    for(int l = 7; l >= 0; l--)
-      if(layer[l] != null)
-	for(int o = layer[l].size()-1; o >= 0; o--)
-	  {
-	    BSXObject obj = (BSXObject)
-	      scene.objects.get(layer[l].elementAt(o));
-	    drawPicture((BSXGraphic)objects.get(layer[l].elementAt(o)),
-			16 * obj.position, 4 * obj.layer);
-	  }
-    paint(getGraphics());
-  }
-  
-public Point Translate(int x, int y, 
-		       int centreX, int centreY, int scale)
-  {
-    return new Point((       x - 127 + centreX)  * 2 * scale / 100,
-                     (255 - (y - 127 + centreY))     * scale / 100);
-  }
-  
-public void drawPicture(BSXGraphic pic) { drawPicture(pic, 127, 127); }
-  
-public void drawPicture(BSXGraphic pic, int position, int layer)
-  {
-    if(pic == null) return;
-    int ap = pic.size();
-    for(int i = 0; i < ap; i++)
-      {
-	BSXPolygon poly = (BSXPolygon)pic.elementAt(i);
-	Polygon p = new Polygon();
-	gBuffer.setColor(poly.getColor());
-	for(int j = poly.npoints - 1; j >= 0; j--)
-	  {
-	    Point pt = Translate(poly.xpoints[j], poly.ypoints[j], 
-				 position, layer, Scale);
-	    p.addPoint(pt.x, pt.y);
-	  }
-	gBuffer.fillPolygon(p);
-      }
-  }
-  
-public boolean mouseEnter(Event evt, int x, int y)
-  {
-    //System.out.println("MOUSE ENTERS");
-    requestFocus();
-    return true;
-  }
-  
-  
-public void update(Graphics g)
-  {
-    paint(g);
-  }
-  
-  
-public void paint(Graphics g)
-  {
-    g.drawImage(imageBuffer, 0, 0, this);
-  }
-}
-
-
-
-
-
-
-
-
diff --git a/web/root/telnet/modules/bsx/BSXGraphic.class b/web/root/telnet/modules/bsx/BSXGraphic.class
deleted file mode 100644
index f6d0d9ffa7d03f9e0a9fcd9a35910f65a426e369..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 573
zcmX^0Z`VEsW(HjbS#|~qb_Pi<21W)p5Wx;2ICvO%88|@%7Y~CN12-cBvxa6EBLj<u
zCx{Z&@X1OnOVrOv%uCnzPs&P7F0lrQOKbS#=BJeAq!#NZ6<6px1xEzr=TxTW=YeI}
zY%=pQOY9gK_??0y+=~(mGBT6(K;|$qusG+Zq%txvdNMNb`();&`jzG;r51%GCgr3u
zGDx5s3e{_kMG9sgBZH7nesW??SYlBo$P}niT*3LJMaijdnILny5>rxKb5e6t^GX;Q
zKpd!MMg{?ptMp4tGIR99Qj<&aix?Ti&^!ip10w@t0V4xrF(U&@Nk(QdgCYYH10w?n
zFf%ZM!jOTHfuDhaL7#yUEY7O6oq>5H0|NsSgCGL~0~=V7mqCPqfq@6iW@V6J5Mp2e
zYX-#xNV6RS69W@SwU+dD1{Q>FF0iNo11kd`*aR^!pABjP$aHB20kFm5VADZnS;5WH
Z-p0U+Fbm=mVI1a2F$jar5e2)P4*-X-WvBoE

diff --git a/web/root/telnet/modules/bsx/BSXGraphic.java b/web/root/telnet/modules/bsx/BSXGraphic.java
deleted file mode 100644
index a1c4155a73..0000000000
--- a/web/root/telnet/modules/bsx/BSXGraphic.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * BSXGraphic	-- BSX Graphics Object
- * --
- * $Id: BSXGraphic.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Wed Feb 14 21:35:42 1996 by Matthias L. Jugel :$
- */
-package modules.bsx;
-
-import java.util.Vector;
-
-public class BSXGraphic extends Vector
-{
-  public BSXGraphic() { super(); }
-  public BSXGraphic(int s) 
-  { 
-		super(s);
-  }
-
-  public void addPolygon(BSXPolygon p) { addElement(p); }
-}
diff --git a/web/root/telnet/modules/bsx/BSXInputStream.class b/web/root/telnet/modules/bsx/BSXInputStream.class
deleted file mode 100644
index 2dbfe7e691ddc059e3763aae5073f501191f541a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1436
zcmX^0Z`VEsW(Hjb4|WDqb_O$c26J`>3w8!8b_Q#91{*F0b_PK%237_kE(RtBVJ-$n
z1`!Zfl#79lK@3EQgCrzC>Lhs>lo{keggl5)<YCZfP~c%OU{K;=aA1(;VQ^xQ<zaAU
zkm6x*VUTBJVAjxd0+XJM3{o1JKDqfRr8%j^`botV`cA<S?nQ|O8JWq}SS17Ub1KvG
z^Q^)8!x$M@G(0uK7#UbKJi)YthEG;vS)zVszP@K(L1{^FNl|KIt~E%Bv<A9GFtb3i
zY&MyBnI(3N48l&q5lC9~K*ljLusG+Zq%tyaxmF~n7L;V>=M^(DFnTgF@cU%urTUfT
zCZ!gIBqrsgGBSu_bpRuS1iI}AZ_1!cA=z(@MJLSNj0{3P`N@enVTnbVAj_dH;0n$!
zElN&x%LF-xBQYf<AU`v&gpq*@%mfD?BLh=n0V4xza(+&J5hDX*1|tJgW*#Gh0LZuc
ziRC5wP&*hI#KA(D`T8!2C5cE5V`LD9$$0w1g9Rp<lbDyTk0i>N!^j|pJ)RjEq|hW#
zY(durcNk*<BLjOuW^zesQ7R(?TLH+E#f%I*MX8A?j=|2Jo*t<cj0}7rE+SDO1mNay
zfK^nMq%tya6sMLrLxO~*BqI|P#}$kWjFk+E3~UTc3?RV5z{sG?pvb_;pvu6&V8y@$
z7H8Aa-p;_ak%57Mi9v&bfq{*Ik%56hh(VWufk6zSg29zR8)N_j8-odhCIce_BPee`
z&DqGn%)rdRz+kPlje*|=MA$B8U<+qnBQ0Q%A}wIKgn>hVeK`XQbBc|OfI*53vWTsK
zfOLjT!WyuZTnxMnEKo-%F>o+2Fc>i~Fc>qiGMF%MF&HuMGMF+5FqknYF_<%$Fj#;c
zXu`k>vKs0@Rt63RV+M5wMg}ehW(G?Jbp|E|0S0ZT7>ETC6J&5?kYQi~2SX0j`AS;b
z7<g@VGq6TVZ)f22UCzL#qqU8JZyN(o3*&$3x&OcDY=8%m5CaPX1A{mND}w|BAA=-=
z0)rITH45N>P=dQgmcbD08a@UQ20I2V1||kI1_lODG%zy=f=U*s8@4krGq5o*FqrRV
zV2|9+z~KXOi!F$-)0W-FAh4W43dAvE0h6rSqAa4U+Zec8nEuPm{r^H6;V>-*E(Qh$
z1qKcVMFw65B?ch|6$WtzRR$>r4F&}UO$IdvEe1UXRR&WA9k7G#Al5=1%)y|~V9TJx
zz{tSMpv7Ph4n83URR$vlbp~bz1qML|HwJYE76vs2ZU%P-bp}=>?=UjxfrCc{0DoN>
A=l}o!

diff --git a/web/root/telnet/modules/bsx/BSXInputStream.java b/web/root/telnet/modules/bsx/BSXInputStream.java
deleted file mode 100644
index 5868a7a8da..0000000000
--- a/web/root/telnet/modules/bsx/BSXInputStream.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * BSXInputStream	-- extends InputStream for BSX Polygons
- * --
- * $Id: BSXInputStream.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Wed Feb 14 21:36:56 1996 by Matthias L. Jugel :$
- */
-package modules.bsx;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import java.awt.Polygon;
-import java.awt.Rectangle;
-
-public class BSXInputStream extends DataInputStream
-{
-  public BSXInputStream(InputStream in) { super(in); }
-  
-  public final BSXGraphic readBSXGraphic()	/* read a BSX graphic */
-    throws IOException
-    {
-      int ap = readASCIIHex();
-      BSXGraphic picture = new BSXGraphic(ap);
-      for(; ap > 0; ap--) picture.addPolygon(readBSXPolygon());
-      return picture;
-    }
-  
-  public final BSXPolygon readBSXPolygon()	/* read a BSX polygon */
-    throws IOException
-    {
-      BSXPolygon p = new BSXPolygon();		/* create new BSX polygon */
-      int points = readASCIIHex();		/* get amount of edges */
-      int color = readASCIIHex();			/* get color */
-      
-      p.setColor(color);
-      for(; points > 0 ; points--)
-	{
-	  int x, y;
-	  x = readASCIIHex();
-	  y = readASCIIHex();
-	  p.addPoint(x, y);
-	}
-      return p;
-    }
-  
-  public int readASCIIHex()			/* read 2 byte ASCII hex val */
-    {
-      int h, l;
-      try {
-	h = (int)readByte();
-	l = (int)readByte();
-      } catch(Exception e) { return 0; }
-      h = h-'0'-(h>'9'?'A'-'9'-1:0);
-      l = l-'0'-(l>'9'?'A'-'9'-1:0);
-      return 16*h+l;
-    }
-}
-
-
diff --git a/web/root/telnet/modules/bsx/BSXObject.class b/web/root/telnet/modules/bsx/BSXObject.class
deleted file mode 100644
index d5a83339d9a99cedaf233b34e84631cde6410b73..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 440
zcmX^0Z`VEsW(HjbId%pyb_Pi<21W*UP6j3h4iLc!BDi=Mcp12P7$g`3co?KXge-$7
zBLlOBW*8#_tA?j1h-S0N%*!mXV`SiS3Xbqk%1TWx(aTCKOJrnVan4UkWn^IVWMts?
z$;?ajE6q(xEec6Y%1LEp5ckQ=PbtkwE!IyeuF!{TwPs`x^2tw5%n3^@$^@x|D(4E$
zFD*(=b;|_lWsG8E5CGYspOcuEt`D)4k%2LXk%2WQu`;!YkwFxz>5L4F1&j<F1^LC9
zC7Jnoj0`L#8JWe54D4l@#hFPtsSJt?Obm<+3=GT+j35s)FfcHJd=I8&8AQP}BLgo3
z4+A3uKLZ0p1_KiVGXn#IoYr;*W-aEe46Go6MT=!C1DlpKh%2*|fn_5D0|OI-AOiz~
z5CaPX0|PGu3j-Ge9|JdoFas}xFaraF0>l&s83rK+5RhY#Vh{kkM2<n6K>+M35wIzI
E0Jy45W&i*H

diff --git a/web/root/telnet/modules/bsx/BSXObject.java b/web/root/telnet/modules/bsx/BSXObject.java
deleted file mode 100644
index f4237f4240..0000000000
--- a/web/root/telnet/modules/bsx/BSXObject.java
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * BSXObject	-- a simple BSX Object
- * --
- * $Id: BSXObject.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- */
-package modules.bsx;
-
-public class BSXObject
-{
-  public int position = 0, layer = 0;
-  public boolean visible = true;
-
-  public BSXObject(int p, int l) { position = p; layer = l; }
-}
diff --git a/web/root/telnet/modules/bsx/BSXPolygon.class b/web/root/telnet/modules/bsx/BSXPolygon.class
deleted file mode 100644
index adcc78c71905f3c32288ac7016aac5d20581f1ae..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2219
zcmX^0Z`VEsW(Hjb19k=tb_Pv$1}!cICI)UU21W)R5Wx%L@^LaSGw^dVFfs^$Sb{tZ
z5)8s1LIgyJf(S7l1_cIL9tIT<p~|4l$RMPl>64XMmZ+atUZU@upOar?&B(y4p&7==
zz@p&^qS!P%J;4kPjcCtk2$K`S1Pim-Waed-*fBEjI|WAs<mXhT=jZ8xjAUeBan4Uk
zWn^IVWMts?$;?ajE6q(xEec6Y%1LEp5Wuuo!Y4OBr8FnCSU;(_LLXtAH6w$NPkwS@
zPFP}5CP+8b2)2M^kc$}^xPtRbi;`2_GC>A2MSC(b2u5QX&zh8zn4HbXz>%Dv12usa
zY5+%iY6;l)j0}7rw<CgskwE}10QDUsgBW&qGBU8|7362;l@v2Fa1_H$WGTtWEM{b2
zuYk$1S3+0}iVVyQObiSRObkp6stn3tmI8w;0|Nsq10yK<7#JC(7#J8IGk~I%fl*g$
zI|GZBfWTe`#z^MfAcC2BI|EyABnyNi$nYP`Wd(5slmr>N1(d)7Y#;$ahIT=QbAk-7
z!9wg1p|b)p0x@6#4v4^iflNV$EnpD=sJ5j7S^`=S5vZ=qf(#%85d&)!$OJ)%04K;K
zW<iGiF!u;>K}G&UoGZW$<HJ?(fJ6it{tF}vxI;|gg^Do4)$l<@1Q}rF@FPT^s<tw4
zX>DTQ+{(bQ5geQ{3=9mD7*rS-7?c@U82A`?82A~)82A_@83Y(q8Tc497z7#282A`0
z8H5--8Tc4{7=#(382A`s8AKRz82A_p7(^NB82A{P7{nMRG4L@=V-ROp%)rO6oI!$N
zD+3?HP6kPaqYQiuCmEy|t~2m4+-8twc*(%W@RmV_;Wq;x!+!=@MqUO!MnMKSMnwib
zMpXtyMjZxa26+Ys1|9}R1_nl523-a@1_p41i$Ej%Cj%!aVK7>bMtGzk!++u;Tnm&K
zq_;D$Vu^f}K@|Ba;K&z)L@+e+85k`Y)EJ~07{SRv9Fh!}WEnUaSQ!`?t+ljuWVbW0
zk7j)$Q-anO1};$+Bb0<NjX{-xfkB;toq>;mpMjr2l7WvwhCzTqgMp7hn?aDll7WxG
zhCztIhk=j5pFx-*mVu8UfkA{JpMj5|m_d}Gk%5n)l|hVQDgz(GOa^g=Wej`_s~990
zb};ZU>|u~(IKjZjaE3vO;T8iQ!#xIRhBpj+3?CR|82&NvF)}g8G72#8F^VwAF{&`|
zF={X<GU_vEFj_IFf)j%YB%0xg!J0vjK?z(sSTpD|D1noTHG?*TGy^jzIe?Q23j-sA
zI)e}-sW9k+lMVv|tJW3<jt%glj}2r9gAUjdaB;)H3bllhL5Bfa+%VWf>mhC}P{2u}
eh8x5PZLksGM9aVpHG+YG8|)HLqE!T2DG31f#-rc>

diff --git a/web/root/telnet/modules/bsx/BSXPolygon.java b/web/root/telnet/modules/bsx/BSXPolygon.java
deleted file mode 100644
index d2384035e9..0000000000
--- a/web/root/telnet/modules/bsx/BSXPolygon.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * BSXPolygon	-- anhanced Polygon (to meet BSX standard)
- *		   includes color for each polygon
- * --
- * $Id: BSXPolygon.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- */
-package modules.bsx;
-
-import java.awt.Polygon;
-import java.awt.Color;
-
-public class BSXPolygon extends Polygon
-{
-  private Color colTable[] = {
-                              new Color(  0,   0,   0),
-			      new Color(  0,   0, 255),
-			      new Color( 34, 139,  34),
-			      new Color(135, 206, 235),
-			      new Color(205,  92,  92),
-			      new Color(255, 105, 180),
-			      new Color(165,  42,  42),
-			      new Color(211, 211, 211),
-			      new Color(105, 105, 105),
-			      new Color(  0, 191, 255),
-			      new Color(  0, 255,   0),
-			      new Color(  0, 255, 255),
-			      new Color(255,  99,  71),
-			      new Color(255,   0, 255),
-			      new Color(255, 255,   0),
-			      new Color(255, 255, 255)
-			     };
-
-  private Color Pcolor = Color.black;
-
-  public BSXPolygon() { super(); }
-  public BSXPolygon(int color) { super(); Pcolor = new Color(color); }
-  public BSXPolygon(int xpoints[], int ypoints[], int npoints, int color)
-         {
-           super(xpoints, ypoints, npoints);
-           Pcolor = colTable[color];
-         }
-
-  public void setColor(int color) { Pcolor = colTable[color]; }
-  public Color getColor() { return Pcolor; }
-}
diff --git a/web/root/telnet/modules/bsx/BSXScene.class b/web/root/telnet/modules/bsx/BSXScene.class
deleted file mode 100644
index 6a28bafe9275eec3d83f333df561f7fc2d1a8569..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 568
zcmX^0Z`VEsW(HjbS#}0-b_NM{21zakMg|Tp1||j$P6lQMP7uMx!@$eH&BGwZAjHEU
z#URMYz^tJe#>gP8;gg%6Qks)mte;d|q3;wN;a-$jkdc{e4U%QE$;`_vv14T5bqbCM
zPEO5B)yqmOOJrnVan4UkWn|#@$;?ajE6q(xEec6Y%1LEp5cL5m&@U~?%+dEqEY2ta
z30pHVNZ_)DkwFYy3T%-zBZH7nesW??SYlBo$PlOnT*3LJMaijdnIPM^k`j}%(~I&;
z^HLZY1i&`uB<7{-`zK|kCYLZW2!n-C9mB{Vg63neeT)q3`Cwhej10^LnaPX{EF~G4
z#SDrJ%nXbS3=B*RjG(|{U|?Vbg&$Oemw}ssk%6Cqfx#WBPE>0<1B=#f2BygE46Iuj
z*tHn9GO%p~D;Hp3VBlk5W?*38XJBFAW#DJvXAogvVBi5;Aj%-aAPhF17c2rYG?0Or
qfr){E0mD#jEQSg|4HW<zDhAOAHI#utltG$7h=GxTkwFyf7(M{S>u#t3

diff --git a/web/root/telnet/modules/bsx/BSXScene.java b/web/root/telnet/modules/bsx/BSXScene.java
deleted file mode 100644
index c81a98627e..0000000000
--- a/web/root/telnet/modules/bsx/BSXScene.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * BSXScene	-- BSX Scene
- * --
- * $Id: BSXScene.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- */
-package modules.bsx;
-
-import java.util.Hashtable;
-
-public class BSXScene
-{
-  public BSXGraphic background;
-  public Hashtable objects = new Hashtable();
-
-  public BSXScene() { background = null; }
-	public BSXScene(BSXGraphic pic) { background = pic; }
-}
diff --git a/web/root/telnet/socket/StatusPeer.class b/web/root/telnet/socket/StatusPeer.class
deleted file mode 100644
index a53275a970599d88ac5c3b8dbd9b3c8b86c60b9d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 277
zcmX^0Z`VEsW(Hjb0d@v%b_QNX1~m<zti-ZJ{nC=m9R0A=<dXa%YfXqyPGVlVzJF3y
zYI2D+BLj<beo87M1HVsZUaDVdZc=JdNMce>DkFmsL^ZMjAi@0P#GJ6iqD+t?s7hg<
z;{4?7)Dr#RlEjkI;(*lDB5OtluHgLAqU2P!Opsyx2q`^~yBHY+K$Jd`TNoL5^72bE
z(<&iK85smIOl4$XD+XJ^$iPyPky*^f$iT$F$N&P2EQ}0149pA+42%q%3=9lR3|s&{
C-A<7J

diff --git a/web/root/telnet/socket/StatusPeer.java b/web/root/telnet/socket/StatusPeer.java
deleted file mode 100644
index cc17b67cb6..0000000000
--- a/web/root/telnet/socket/StatusPeer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Status peer interface.
- * --
- * $Id: StatusPeer.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Wed Mar  5 13:40:54 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package socket;
-
-import java.util.Vector;
-
-/**
- * StatusPeer -- interface for status messages
- * --
- * @version	$Id: StatusPeer.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author	Matthias L. Jugel, Marcus Mei�ner
- */
-
-public interface StatusPeer
-{
-	/**
-	 * This method is called for the peer of the TelnetIO class if there is
-	 * a statuschange.
-	 * @param status A Vector containing the key as element 0 and any arguments
-	 *               from element 1 on.
-	 * @return an object that matches the requested information or null
-	 * @see socket.TelnetIO
-	 */
-  public Object notifyStatus(Vector status);
-}
diff --git a/web/root/telnet/socket/TelnetIO.class b/web/root/telnet/socket/TelnetIO.class
deleted file mode 100644
index bcd3e99f7fa8e679082d45cdd157c2c662cc9e07..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 9052
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
HV&nw?9<Vra

diff --git a/web/root/telnet/socket/TelnetIO.java b/web/root/telnet/socket/TelnetIO.java
deleted file mode 100644
index 87da79ff51..0000000000
--- a/web/root/telnet/socket/TelnetIO.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/**
- * socket.TelnetIO - a telnet implementation
- * --
- * $Id: TelnetIO.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Tue May 27 13:27:05 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package socket;
-
-import java.net.Socket;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.awt.Dimension;
-import java.util.Vector;
-
-/**
- * Implements simple telnet io
- *
- * @version $Id: TelnetIO.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- * @version 1.2 3/7/97 George Ruban added available() because it was needed.
- */
-public class TelnetIO implements StatusPeer
-{
-  /**
-   * Return the version of TelnetIO.
-   */
-  public String toString() { return "$Id: TelnetIO.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $"; }
-  
-	/**
-	 * Debug level. This results in additional diagnostic messages on the
-	 * java console.
-	 */
-	private static int debug = 0;
-
-	/**
-	 * State variable for telnetnegotiation reader
-	 */
-	private byte neg_state = 0;
-
-	/**
-	 * constants for the negotiation state
-	 */
-	private final static byte STATE_DATA	= 0;
-	private final static byte STATE_IAC	= 1;
-	private final static byte STATE_IACSB	= 2;
-	private final static byte STATE_IACWILL	= 3;
-	private final static byte STATE_IACDO	= 4;
-	private final static byte STATE_IACWONT	= 5;
-	private final static byte STATE_IACDONT	= 6;
-	private final static byte STATE_IACSBIAC	= 7;
-	private final static byte STATE_IACSBDATA	= 8;
-	private final static byte STATE_IACSBDATAIAC	= 9;
-
-	/**
-	 * What IAC SB <xx> we are handling right now
-	 */
-	private byte current_sb;
-
-	/**
-	 * IAC - init sequence for telnet negotiation.
-	 */
-	private final static byte IAC  = (byte)255;
-	/**
-	 * [IAC] End Of Record
-	 */
-	private final static byte EOR  = (byte)239;
-	/**
-	 * [IAC] WILL
-	 */
-	private final static byte WILL  = (byte)251;
-	/**
-	 * [IAC] WONT
-	 */
-	private final static byte WONT  = (byte)252;
-	/**
-	 * [IAC] DO
-	 */
-	private final static byte DO    = (byte)253;
-	/**
-	 * [IAC] DONT
-	 */
-	private final static byte DONT  = (byte)254;
-	/**
-	 * [IAC] Sub Begin 
-	 */
-	private final static byte SB  = (byte)250;
-	/**
-	 * [IAC] Sub End
-	 */
-	private final static byte SE  = (byte)240;
-	/**
-	 * Telnet option: echo text
-	 */
-	private final static byte TELOPT_ECHO  = (byte)1;  /* echo on/off */
-	/**
-	 * Telnet option: End Of Record
-	 */
-	private final static byte TELOPT_EOR   = (byte)25;  /* end of record */
-	/**
-	 * Telnet option: Negotiate About Window Size
-	 */
-	private final static byte TELOPT_NAWS  = (byte)31;  /* NA-WindowSize*/
-	/**
-	 * Telnet option: Terminal Type
-	 */
-	private final static byte TELOPT_TTYPE  = (byte)24;  /* terminal type */
-
-	private final static byte[] IACWILL  = { IAC, WILL };
-	private final static byte[] IACWONT  = { IAC, WONT };
-	private final static byte[] IACDO    = { IAC, DO	};
-	private final static byte[] IACDONT  = { IAC, DONT };
-	private final static byte[] IACSB  = { IAC, SB };
-	private final static byte[] IACSE  = { IAC, SE };
-
-	/** 
-	 * Telnet option qualifier 'IS' 
-	 */
-	private final static byte TELQUAL_IS = (byte)0;
-
-	/** 
-	 * Telnet option qualifier 'SEND' 
-	 */
-	private final static byte TELQUAL_SEND = (byte)1;
-
-	/**
-	 * What IAC DO(NT) request do we have received already ?
-	 */
-        private byte[] receivedDX;
-  
-	/**
-	 * What IAC WILL/WONT request do we have received already ?
-	 */
-	private byte[] receivedWX;
-	/**
-	 * What IAC DO/DONT request do we have sent already ?
-	 */
-	private byte[] sentDX;
-	/**
-	 * What IAC WILL/WONT request do we have sent already ?
-	 */
-	private byte[] sentWX;
-
-	private Socket socket;
-	private BufferedInputStream is;
-	private BufferedOutputStream os;
-
-	private StatusPeer peer = this;		/* peer, notified on status */
-
-	/**
-	 * Connect to the remote host at the specified port.
-	 * @param address the symbolic host address
-	 * @param port the numeric port
-	 * @see #disconnect
-	 */
-	public void connect(String address, int port) throws IOException {
-		if(debug > 0) System.out.println("Telnet.connect("+address+","+port+")");
-		socket = new Socket(address, port);
-		is = new BufferedInputStream(socket.getInputStream());
-		os = new BufferedOutputStream(socket.getOutputStream());
-		neg_state = 0;
-		receivedDX = new byte[256]; 
-		sentDX = new byte[256];
-		receivedWX = new byte[256]; 
-		sentWX = new byte[256];
-	}
-
-	/**
-	 * Disconnect from remote host.
-	 * @see #connect
-	 */
-	public void disconnect() throws IOException {
-	  if(debug > 0) System.out.println("TelnetIO.disconnect()");
-	  if(socket !=null) socket.close();
-	}
-  
-	/**
-	 * Connect to the remote host at the default telnet port (23).
-	 * @param address the symbolic host address
-	 */
-	public void connect(String address) throws IOException {
-		connect(address, 23);
-	}
-
-	/**
-	 * Set the object to be notified about current status.
-	 * @param obj object to be notified.
-	 */
-	public void setPeer(StatusPeer obj) { peer = obj; }
-
-	/** Returns bytes available to be read.  Since they haven't been
-	 * negotiated over, this could be misleading.
-	 * Most useful as a boolean value - "are any bytes available" -
-	 * rather than as an exact count of "how many ara available."
-	 *
-	 * @exception IOException on problems with the socket connection
-	 */
-	public int available() throws IOException
-	{
-	  return is.available();
-	}
-	
-
-	/**
-	 * Read data from the remote host. Blocks until data is available. 
-	 * Returns an array of bytes.
-	 * @see #send
-	 */
-	public byte[] receive() throws IOException {
-		int count = is.available();
-		byte buf[] = new byte[count];
-		count = is.read(buf);
-		if(count < 0) throw new IOException("Connection closed.");
-		if(debug > 1) System.out.println("TelnetIO.receive(): read bytes: "+count);
-		buf = negotiate(buf, count);
-		return buf;
-	}
-
-	/**
-	 * Send data to the remote host.
-	 * @param buf array of bytes to send
-	 * @see #receive
-	 */
-	public void send(byte[] buf) throws IOException {
-		if(debug > 1) System.out.println("TelnetIO.send("+buf+")");
-		os.write(buf);
-		os.flush();
-	}
-
-	public void send(byte b) throws IOException {
-		if(debug > 1) System.out.println("TelnetIO.send("+b+")");
-		os.write(b);
-		os.flush();
-	}
-
-	/**
-	 * Handle an incoming IAC SB <type> <bytes> IAC SE
-	 * @param type type of SB
-	 * @param sbata byte array as <bytes>
-	 * @param sbcount nr of bytes. may be 0 too.
-	 */
-	private void handle_sb(byte type, byte[] sbdata, int sbcount) 
-		throws IOException 
-	{
-		if(debug > 1) 
-			System.out.println("TelnetIO.handle_sb("+type+")");
-		switch (type) {
-		case TELOPT_TTYPE:
-			if (sbcount>0 && sbdata[0]==TELQUAL_SEND) {
-				String ttype;
-				send(IACSB);send(TELOPT_TTYPE);send(TELQUAL_IS);
-				/* FIXME: need more logic here if we use 
-				 * more than one terminal type
-				 */
-				Vector vec = new Vector(2);
-				vec.addElement("TTYPE");
-				ttype = (String)peer.notifyStatus(vec);
-				if(ttype == null) ttype = "dumb";
-				byte[] bttype = new byte[ttype.length()];
-
-				ttype.getBytes(0,ttype.length(), bttype, 0);
-				send(bttype);
-				send(IACSE);
-			}
-
-		}
-	}
-
-	/**
-	 * Notify about current telnet status. This method is called top-down.
-	 * @param status contains status information
-	 */
-	public Object notifyStatus(Vector status) {
-		if(debug > 0) 
-		  System.out.println("TelnetIO.notifyStatus("+status+")");
-		return null;
-	}
-
-	/* wo faengt buf an bei buf[0] oder bei buf[1] */
-	private byte[] negotiate(byte buf[], int count) throws IOException {
-		if(debug > 1) 
-			System.out.println("TelnetIO.negotiate("+buf+","+count+")");
-		byte nbuf[] = new byte[count];
-		byte sbbuf[] = new byte[count];
-		byte sendbuf[] = new byte[3];
-		byte b,reply;
-		int  sbcount = 0;
-		int boffset = 0, noffset = 0;
-		Vector	vec = new Vector(2);
-
-		while(boffset < count) {
-			b=buf[boffset++];
-			/* of course, byte is a signed entity (-128 -> 127)
-			 * but apparently the SGI Netscape 3.0 doesn't seem
-			 * to care and provides happily values up to 255
-			 */
-			if (b>=128)
-				b=(byte)((int)b-256);
-			switch (neg_state) {
-			case STATE_DATA:
-				if (b==IAC) {
-					neg_state = STATE_IAC;
-				} else {
-					nbuf[noffset++]=b;
-				}
-				break;
-			case STATE_IAC:
-				switch (b) {
-				case IAC:
-					if(debug > 2) 
-						System.out.print("IAC ");
-					neg_state = STATE_DATA;
-					nbuf[noffset++]=IAC;
-					break;
-				case WILL:
-					if(debug > 2)
-						System.out.print("WILL ");
-					neg_state = STATE_IACWILL;
-					break;
-				case WONT:
-					if(debug > 2)
-						System.out.print("WONT ");
-					neg_state = STATE_IACWONT;
-					break;
-				case DONT:
-					if(debug > 2)
-						System.out.print("DONT ");
-					neg_state = STATE_IACDONT;
-					break;
-				case DO:
-					if(debug > 2)
-						System.out.print("DO ");
-					neg_state = STATE_IACDO;
-					break;
-				case EOR:
-					if(debug > 2)
-						System.out.print("EOR ");
-					neg_state = STATE_DATA;
-					break;
-				case SB:
-					if(debug > 2)
-						System.out.print("SB ");
-					neg_state = STATE_IACSB;
-					sbcount = 0;
-					break;
-				default:
-					if(debug > 2)
-						System.out.print(
-							"<UNKNOWN "+b+" > "
-						);
-					neg_state = STATE_DATA;
-					break;
-				}
-				break;
-			case STATE_IACWILL:
-				switch(b) {
-				case TELOPT_ECHO:
-					if(debug > 2) 
-						System.out.println("ECHO");
-					reply = DO;
-					vec = new Vector(2);
-					vec.addElement("NOLOCALECHO");
-					peer.notifyStatus(vec);
-					break;
-				case TELOPT_EOR:
-					if(debug > 2) 
-						System.out.println("EOR");
-					reply = DO;
-					break;
-				default:
-					if(debug > 2)
-						System.out.println(
-							"<UNKNOWN,"+b+">"
-						);
-					reply = DONT;
-					break;
-				}
-				if(debug > 1)
-				  System.out.println("<"+b+", WILL ="+WILL+">");
-				if (	reply != sentDX[b+128] ||
-					WILL != receivedWX[b+128]
-				) {
-					sendbuf[0]=IAC;
-					sendbuf[1]=reply;
-					sendbuf[2]=b;
-					send(sendbuf);
-					sentDX[b+128] = reply;
-					receivedWX[b+128] = WILL;
-				}
-				neg_state = STATE_DATA;
-				break;
-			case STATE_IACWONT:
-				switch(b) {
-				case TELOPT_ECHO:
-					if(debug > 2) 
-						System.out.println("ECHO");
-
-					vec = new Vector(2);
-					vec.addElement("LOCALECHO");
-					peer.notifyStatus(vec);
-					reply = DONT;
-					break;
-				case TELOPT_EOR:
-					if(debug > 2) 
-						System.out.println("EOR");
-					reply = DONT;
-					break;
-				default:
-					if(debug > 2) 
-						System.out.println(
-							"<UNKNOWN,"+b+">"
-						);
-					reply = DONT;
-					break;
-				}
-				if (	reply != sentDX[b+128] ||
-					WONT != receivedWX[b+128]
-				) {
-					sendbuf[0]=IAC;
-					sendbuf[1]=reply;
-					sendbuf[2]=b;
-					send(sendbuf);
-					sentDX[b+128] = reply;
-					receivedWX[b+128] = WILL;
-				}
-				neg_state = STATE_DATA;
-				break;
-			case STATE_IACDO:
-				switch (b) {
-				case TELOPT_ECHO:
-					if(debug > 2) 
-						System.out.println("ECHO");
-					reply = WILL;
-					vec = new Vector(2);
-					vec.addElement("LOCALECHO");
-					peer.notifyStatus(vec);
-					break;
-				case TELOPT_TTYPE:
-					if(debug > 2) 
-						System.out.println("TTYPE");
-					reply = WILL;
-					break;
-				case TELOPT_NAWS:
-					if(debug > 2) 
-						System.out.println("NAWS");
-					vec = new Vector(2);
-					vec.addElement("NAWS");
-					Dimension size = (Dimension)
-						peer.notifyStatus(vec);
-					receivedDX[b] = DO;
-					if(size == null)
-					{
-						/* this shouldn't happen */
-						send(IAC);
-						send(WONT);
-						send(TELOPT_NAWS);
-						reply = WONT;
-						sentWX[b] = WONT;
-						break;
-					}
-					reply = WILL;
-					sentWX[b] = WILL;
-					sendbuf[0]=IAC;
-					sendbuf[1]=WILL;
-					sendbuf[2]=TELOPT_NAWS;
-					send(sendbuf);
-					send(IAC);send(SB);send(TELOPT_NAWS);
-					send((byte) (size.width >> 8));
-					send((byte) (size.width & 0xff));
-					send((byte) (size.height >> 8));
-					send((byte) (size.height & 0xff));
-					send(IAC);send(SE);
-					break;
-				default:
-					if(debug > 2) 
-						System.out.println(
-							"<UNKNOWN,"+b+">"
-						);
-					reply = WONT;
-					break;
-				}
-				if (	reply != sentWX[128+b] ||
-					DO != receivedDX[128+b]
-				) {
-					sendbuf[0]=IAC;
-					sendbuf[1]=reply;
-					sendbuf[2]=b;
-					send(sendbuf);
-					sentWX[b+128] = reply;
-					receivedDX[b+128] = DO;
-				}
-				neg_state = STATE_DATA;
-				break;
-			case STATE_IACDONT:
-				switch (b) {
-				case TELOPT_ECHO:
-					if(debug > 2) 
-						System.out.println("ECHO");
-					reply	= WONT;
-					vec = new Vector(2);
-					vec.addElement("NOLOCALECHO");
-					peer.notifyStatus(vec);
-					break;
-				case TELOPT_NAWS:
-					if(debug > 2) 
-						System.out.println("NAWS");
-					reply	= WONT;
-					break;
-				default:
-					if(debug > 2) 
-						System.out.println(
-							"<UNKNOWN,"+b+">"
-						);
-					reply	= WONT;
-					break;
-				}
-				if (	reply	!= sentWX[b+128] ||
-					DONT	!= receivedDX[b+128]
-				) {
-					send(IAC);send(reply);send(b);
-					sentWX[b+128]		= reply;
-					receivedDX[b+128]	= DONT;
-				}
-				neg_state = STATE_DATA;
-				break;
-			case STATE_IACSBIAC:
-				if(debug > 2) System.out.println(""+b+" ");
-				if (b == IAC) {
-					sbcount		= 0;
-					current_sb	= b;
-					neg_state	= STATE_IACSBDATA;
-				} else {
-					System.out.println("(bad) "+b+" ");
-					neg_state	= STATE_DATA;
-				}
-				break;
-			case STATE_IACSB:
-				if(debug > 2) System.out.println(""+b+" ");
-				switch (b) {
-				case IAC:
-					neg_state = STATE_IACSBIAC;
-					break;
-				default:
-					current_sb	= b;
-					sbcount		= 0;
-					neg_state	= STATE_IACSBDATA;
-					break;
-				}
-				break;
-			case STATE_IACSBDATA:
-				if (debug > 2) System.out.println(""+b+" ");
-				switch (b) {
-				case IAC:
-					neg_state = STATE_IACSBDATAIAC;
-					break;
-				default:
-					sbbuf[sbcount++] = b;
-					break;
-				}
-				break;
-			case STATE_IACSBDATAIAC:
-				if (debug > 2) System.out.println(""+b+" ");
-				switch (b) {
-				case IAC:
-					neg_state = STATE_IACSBDATA;
-					sbbuf[sbcount++] = IAC;
-					break;
-				case SE:
-					handle_sb(current_sb,sbbuf,sbcount);
-					current_sb	= 0;
-					neg_state	= STATE_DATA;
-					break;
-				case SB:
-					handle_sb(current_sb,sbbuf,sbcount);
-					neg_state	= STATE_IACSB;
-					break;
-				default:
-					neg_state	= STATE_DATA;
-					break;
-				}
-				break;
-			default:
-				if (debug > 2) 
-					System.out.println(
-						"This should not happen: "+
-						neg_state+" "
-					);
-				neg_state = STATE_DATA;
-				break;
-			}
-		}
-		buf	= new byte[noffset];
-		System.arraycopy(nbuf, 0, buf, 0, noffset);
-		return buf;
-	}
-}
diff --git a/web/root/telnet/socket/TelnetWrapper.class b/web/root/telnet/socket/TelnetWrapper.class
deleted file mode 100644
index afd702d1c44a904c9966ced29ae10322d12a0098..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 5705
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

diff --git a/web/root/telnet/socket/TelnetWrapper.java b/web/root/telnet/socket/TelnetWrapper.java
deleted file mode 100644
index a7f6c7fb2a..0000000000
--- a/web/root/telnet/socket/TelnetWrapper.java
+++ /dev/null
@@ -1,396 +0,0 @@
-package socket;
-
-import java.io.IOException;
-import java.util.Date;
-
-/** Wrapper for a Java Telnet call. 
- * To use, make a new TelnetWrapper() with the name or IP address of a host.
- * Then, for most uses, the easiest way is to call setPrompt() with the
- * expected prompt, then call login(), and a sequence of sendLine()'s
- * until you get what you want done.
- * <P>
- * If you don't know the prompt ahead of time, you have to do a sequence of
- * send() and wait() or receiveUntil() calls.  send() sends a string across
- * the telnet connection. Add a '\r' to the end if you want to
- * complete a command. wait() waits for an exact string from the other side
- * of the telnet connection, and returns nothing,
- * receiveUntil() also waits for a string, but returns all the data
- * that it received while waiting, including the string itself. 
- * Use this if you want the output from a command. Please note that
- * the telnet connection will usually echo the sent command. 
- * <P>
- * sendLine() is generally better, since it adds the '\r'
- * automatically, waits for the prompt before returning, and returns all
- * data received before the prompt, with the prompt itself cut off the
- * end, and the sent command cut off the beginning. login() and
- * sendLine() are implemented using send(), wait() and receiveUntil().
- * They can be freely mixed and matched.
- * <P>
- * Here is a simple example of the use of TelnetWrapper:
- * <PRE>
- * // creates a new file in /tmp, lists the directory to prove it done
- * {
- *   TelnetWrapper telnet = new TelnetWrapper("123.45.78.90");
- *
- *   // setting the correct prompt ahead of time is very important 
- *   // if you want to use login and sendLine
- *   telnet.setPrompt("$ ");
- *   telnet.login("loginname", "password");
- *
- *   // this is how you have to do it otherwise
- *   telnet.send("touch /tmp/TELNET_WRAPPER" + "\r");
- *   telnet.wait("$ ");
- *
- *   // sendLine 1: adds the \r automatically, 2: waits for the prompt
- *   // before returning 3: returns what was printed from the command
- *   String ls = telnet.sendLine("ls /tmp");
- *   System.out.println(ls);
- *
- *   // clean up
- *   telnet.disconnect();
- * } 
- * </PRE>
- * @author George Ruban 3/4/97
- * @version 0.2 5/15/97 - added comments, replaced String += with
- *    StringBuffer.append() in receiveUntil(), added port constructor
- * @version 0.3 7/30/97 - added optional timeout to receiveUntil() and wait()
- * @see TelnetIO
- */
-public class TelnetWrapper
-{
-  /** The telnet connection. That which is wrapped. */
-  TelnetIO tio;
-  /** Set to true for System.out.println debugging. */
-  public boolean debug = false;
-  /** The current prompt on the remote system. */
-  private String prompt;
-
-  /** The default prompt used by all TelnetWrappers unless specifically
-   * overridden.
-   * @see #setPrompt
-   */
-  private static String defaultPrompt = "$ ";
-
-  /** The default login name used by TelnetWrappers.
-   * If defaultLogin and defaultPassword are both non-null
-   * when a TelnetWrapper is created, the TelnetWrapper will attempt
-   * to login.
-   */
-  private static String defaultLogin = null;
-
-  /** The default password used by TelnetWrappers.
-   * If defaultLogin and defaultPassword are both non-null
-   * when a TelnetWrapper is created, the TelnetWrapper will attempt
-   * to login.
-   */
-  private static String defaultPassword = null;
-  
-  /** Skip any received data until the token appears. 
-   * More efficient than receiveUntil, but liable to fail on large
-   * tokens that can be spread over several "send"s. In that case,
-   * consider using receiveUntil and ignoring the return value.
-   * @param token String to wait for
-   * @exception IOException on problems with the socket connection
-   * @see #receiveUntil
-   */
-  public void wait(String token) throws IOException
-  {
-    wait(token, -1);
-  }
-
-  /** Wait for a String or a timeout. 
-   * If time runs out, throws a TimedOutException.
-   * Sleeps in intervals of 100 milliseconds until either receiving the
-   * token or timeout.
-   * <P>
-   * More efficient than receiveUntil, but liable to fail on large
-   * tokens that can be spread over several "send"s. In that case,
-   * consider using receiveUntil and ignoring the return value.
-   * @param token String to wait for
-   * @param timeout time in milliseconds to wait (negative means wait forever)
-   * @exception IOException on problems with the socket connection
-   * @exception TimedOutException if time runs out before token received
-   * @see #receiveUntil(String, long)
-   */
-  public void wait(String token, long timeout) 
-    throws IOException, TimedOutException
-  {
-    if(debug) System.out.println("wait(" + token + ", " + timeout + ")...");
-    String tmp = "";
-    long deadline = 0;
-    if(timeout >= 0) 
-      deadline = new Date().getTime() + timeout;
-    
-    do {
-      if(timeout >= 0)
-      {
-	while(available() <= 0)
-	{
-	  if(new Date().getTime() > deadline) 
-	    throw new TimedOutException();
-	  try{
-	    Thread.currentThread().sleep(100);
-	  }
-	  catch(InterruptedException ignored)
-	  {}
-	}
-      }
-      tmp = receive();
-    } while(tmp.indexOf(token) == -1);
-    if(debug) System.out.println("wait(" + token  + ", " + timeout + 
-				 ") successful.");
-  }
-
-  /** Returns bytes available to be read.  Since they haven't been
-   * negotiated over, this could be misleading...
-   */
-  public int available() throws IOException
-  {
-    return tio.available();
-  }
-	
-  /** Returns a String from the telnet connection. Blocks
-   * until one is available. No guarantees that the string is in
-   * any way complete.
-   * NOTE: uses Java 1.0.2 style String-bytes conversion.*/
-  public String receive() throws IOException
-  {
-    String s = new String(receiveBytes(), 0);
-    if(debug) System.out.println(s);
-    return s;
-  }
-
-  /** Returns a byte array. Blocks until data is available. */
-  public byte[] receiveBytes() throws IOException
-  {
-    return tio.receive();
-  }
-
-  /** Returns all data received up until a certain token. 
-   * @param token String to wait for
-   * @exception IOException on problems with the socket connection
-   * @see #wait
-   */
-  public String receiveUntil(String token) throws IOException
-  {
-    return receiveUntil(token, -1);
-  }
-  
-
-  /** Returns all data received up until a certain token. 
-   * @param token String to wait for
-   * @param timeout time in milliseconds to wait (negative means wait forever)
-   * @exception IOException on problems with the socket connection
-   * @exception TimedOutException if time runs out before token received
-   * @see #wait(String, long)
-   */
-  public String receiveUntil(String token, long timeout) 
-    throws IOException, TimedOutException
-  {
-    StringBuffer buf = new StringBuffer();
-    long deadline = 0;
-    if(timeout >= 0) 
-      deadline = new Date().getTime() + timeout;
-    do
-    {
-      if(timeout >= 0)
-      {
-	while(available() <= 0)
-	{
-	  if(new Date().getTime() > deadline) 
-	    throw new TimedOutException();
-	  try{
-	    Thread.currentThread().sleep(100);
-	  }
-	  catch(InterruptedException ignored)
-	  {}
-	}
-      }
-      buf.append(receive());
-    } while(buf.toString().indexOf(token) == -1);
-    return buf.toString();
-  }
-  
-  /** Sends a String to the remote host.
-   * NOTE: uses Java 1.0.2 style String-bytes conversion.
-   * @exception IOException on problems with the socket connection
-   */
-  public void send(String s) throws IOException
-  {
-    if(debug) System.out.println(s);
-    byte[] buf = new byte[s.length()];
-    s.getBytes(0, buf.length, buf, 0);
-    tio.send(buf);
-  }
-
-  /** Sends a line to the remote host, returns all data before the prompt.
-   * Since telnet seems to rely on carriage returns ('\r'), 
-   * one will be appended to the sent string, if necessary.
-   * @param command command line to send
-   * @return whatever data the command produced before the prompt.
-   * @see #setPrompt
-   */
-  public String sendLine(String command) throws IOException
-  {
-    if(command.charAt(command.length() -1) != '\r') 
-      command += "\r";
-    send(command);
-    String s = receiveUntil(prompt);
-
-    // telnet typically echoes the command with a \r\n ending...
-    return s.substring(command.length() + 1, s.indexOf(prompt));
-  }
-  
-  /** Sends bytes over the telnet connection. */
-  public void send(byte[] buf) throws IOException
-  {
-    tio.send(buf);
-  }
-  
-  /** Logs in as a particular user and password. 
-    * Returns after receiving prompt. */
-  public void login(String loginName, String password) throws IOException
-  {
-    wait("login:");
-    send(loginName + "\r");
-    wait("Password:");
-    sendLine(password + "\r");
-  }
-    
-  /** Connects to the default telnet port on the given host. 
-   * If the defaultLogin and defaultPassword are non-null, attempts login. */
-  public TelnetWrapper(String host) throws IOException
-  {
-    tio = new TelnetIO();
-    setPrompt(defaultPrompt);
-    tio.connect(host);
-    if(defaultLogin != null && defaultPassword != null)
-    {
-      login(defaultLogin, defaultPassword);
-    }
-  }
-
-  /** Connects to a specific telnet port on the given host. 
-   * If the defaultLogin and defaultPassword are non-null, attempts login. */
-  public TelnetWrapper(String host, int port) throws IOException
-  {
-    tio = new TelnetIO();
-    setPrompt(defaultPrompt);
-    tio.connect(host, port);
-    if(defaultLogin != null && defaultPassword != null)
-    {
-      login(defaultLogin, defaultPassword);
-    }
-  }
-  
-  /** Sets the expected prompt. 
-   * If this function is not explicitly called, the default prompt is used.
-   * @see #setDefaultPrompt
-   */
-  public void setPrompt(String prompt)
-  {
-    if(prompt == null) throw new IllegalArgumentException("null prompt.");
-    this.prompt = prompt;
-  }
-
-  /** Sets the default prompt used by all TelnetWrappers.
-   * This can be specifically overridden for a specific instance.
-   * The default prompt starts out as "$ " until this function is called.
-   * @see #setPrompt
-   */
-  public static void setDefaultPrompt(String prompt)
-  {
-    if(prompt == null) throw new IllegalArgumentException("null prompt.");
-    defaultPrompt = prompt;
-  }
-
-  /** Sets the default login used by TelnetWrappers.
-   * If this method is called with non-null login and password,
-   * all TelnetWrappers will attempt to login when first created.
-   * @param login login name to use
-   * @param password password to use
-   * @see #login
-   * @see #unsetLogin
-   */
-  public static void setLogin(String login, String password)
-  {
-    if(login == null || password == null)
-      throw new IllegalArgumentException("null login or password.");
-    defaultLogin = login;
-    defaultPassword = password;
-  }
-
-
-  /** Turns off the default login of TelnetWrappers.
-   * After this method is called, TelnetWrappers will not
-   * login until that method is explicitly called.
-   * @see #setLogin
-   * @see #login
-   */
-  public static void unsetLogin()
-  {
-    defaultLogin = defaultPassword = null;
-  }
-  
-  /** Ends the telnet connection. */
-  public void disconnect() throws IOException
-  {
-    if(tio != null) tio.disconnect();
-    tio = null;
-  }
-  
-  /** Ends the telnet connection. */
-  public void finalize()
-  {
-    try
-    {
-      disconnect();
-    }
-    catch(IOException e)
-    {} // after all, what can be done at this point?
-  }  
-
-  /** Telnet test driver.
-   * Modeled after the IOtest.java example in the Telnet Applet.
-   * Logs in to "host", creates a timestamped file in /tmp, lists the
-   * /tmp directory to System.out, disconnects.  Shows off several
-   * TelnetWrapper methods.
-   * @param args host login password prompt
-   */
-  public static void main(String args[]) throws IOException
-  {
-    if(args.length != 4) throw new 
-      IllegalArgumentException("Usage: TelnetWrapper host login password prompt");
-    
-    String host = args[0];
-    String login = args[1];
-    String password = args[2];
-    String prompt = args[3];
-
-    Date now = new Date();
-    String timestamp = now.getYear() + "-" +
-		(now.getMonth()+1) + "-" + now.getDate() + "-" +
-		  now.getHours() + ":" + now.getMinutes() + ":" +
-		    now.getSeconds();
-    TelnetWrapper telnet = new TelnetWrapper(host);
-    telnet.debug = true;
-
-    // setting the correct prompt ahead of time is very important 
-    // if you want to use login and sendLine
-    telnet.setPrompt(prompt);
-    telnet.login(login, password);
-
-    // this is how you have to do it otherwise
-    telnet.send("touch /tmp/TELNET_WRAPPER-" + timestamp + "\r");
-    telnet.wait(prompt);
-
-    // sendLine 1: adds the \r automatically, 2: waits for the prompt
-    // before returning 3: returns what was printed from the command
-    String ls = telnet.sendLine("ls /tmp");
-    System.out.println(ls);
-
-    // clean up
-    telnet.disconnect();
-  }
-}
-
diff --git a/web/root/telnet/socket/TimedOutException.class b/web/root/telnet/socket/TimedOutException.class
deleted file mode 100644
index 274c0796a57b759a398048452383233ec17c6112..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 434
zcmX^0Z`VEsW(HjbQFaDFb_QWC21W)}5W&X7z{$W4A~+Zsm^C!R7#T!0e6kYD67_Qu
z^V0Q$ONuh{(yc+_Y&MyBnI(3N3@pz1DXEMM{63j^seYxoNvTC4iAg!Bj0{4U8bN~j
z$%#2(iA9+pMNpMeKE?UT*{LP^A(^?UDgLD;t`*6t1tppJdDe^!T*3LJMaijdnIJR8
zFjVM)T*t^D45IWi^YuOb;hGs4*mF~hixbmR85tyS*}%xaQj(Eb%%I4?#K6b^0w9``
zft`Vofs28GL7#yUEY7O6oq>5H0|NsS12+Q$0~=V7i-DJcfq@6iW@Qjz;A3E5U}WHA
x;DBnjV_;%n0;$&0-p;^+(9H!F6<}ax-~*c=2IjLtO#qoL1U8kCfgfz6005DWRZ;)|

diff --git a/web/root/telnet/socket/TimedOutException.java b/web/root/telnet/socket/TimedOutException.java
deleted file mode 100644
index ac9621714d..0000000000
--- a/web/root/telnet/socket/TimedOutException.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package socket;
-import java.io.IOException;
-
-/** Exception thrown when a Telnet connection takes too long
- * before receiving a specified String token.
- * @author George Ruban
- * @version 0.1 7/30/97 */
-public class TimedOutException extends IOException
-{
-  public TimedOutException()
-  {
-  }
-
-  public TimedOutException(String message)
-  {
-    super(message);
-  }
-}
-
diff --git a/web/root/telnet/telnet.class b/web/root/telnet/telnet.class
deleted file mode 100644
index fb4bd00a58ea4ab22ff3c73f40e1ae680beb76f6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 10936
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

diff --git a/web/root/telnet/telnet.java b/web/root/telnet/telnet.java
deleted file mode 100644
index e99235295b..0000000000
--- a/web/root/telnet/telnet.java
+++ /dev/null
@@ -1,576 +0,0 @@
-/**
- * telnet -- implements a simple telnet
- * --
- * $Id: telnet.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Mon Aug  4 13:11:14 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-import java.applet.Applet;
-import java.awt.Frame;
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.Panel;
-import java.awt.Event;
-import java.util.Vector;
-import java.util.Hashtable;
-import java.util.Enumeration;
-import java.io.IOException;
-
-import socket.TelnetIO;
-import socket.StatusPeer;
-
-import display.Terminal;
-import display.TerminalHost;
-
-import modules.Module;
-
-/**
- * A telnet implementation that supports different terminal emulations.
- * @version $Id: telnet.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * @author  Matthias L. Jugel, Marcus Mei�ner
- */
-public class telnet extends Applet implements Runnable, TerminalHost, StatusPeer
-{
-  /**
-   * The telnet io methods.
-   * @see socket.TelnetIO
-   */
-  protected TelnetIO tio;
-
-  /**
-   * The terminal emulation (dynamically loaded).
-   * @see emulation
-   * @see display.Terminal
-   * @see display.TerminalHost
-   */
-  protected Terminal term;
-
-  /**
-   * The host address to connect to. This is retrieved from the PARAM tag
-   * "address".
-   */
-  protected String address;
-
-  /**
-   * The port number (default ist 23). This can be specified as the PARAM tag
-   * "port".
-   */
-  protected int port = 23;
-
-  /**
-   * The proxy ip address. If this variable is set telnet will try to connect
-   * to this address and then send a string to tell the relay where the
-   * target host is.
-   * @see address
-   */
-  protected String proxy = null;
-  /**
-   * The proxy port number. This is the port where the relay is expected to
-   * listen for incoming connections.
-   * @see proxy
-   * @see port
-   */
-  protected int proxyport;
-  
-  /**
-   * Emulation type (default is vt320). This can be specified as the PARAM
-   * tag "emulation".
-   * @see term
-   * @see display.Terminal
-   * @see display.TerminalHost
-   */
-  protected String emulation = "vt320";
-
-  /**
-   * Dynamically loaded modules are stored here.
-   */
-  protected Vector modules = null;
-
-  // some state variables;
-  private boolean localecho = true;
-  private boolean connected = false;
-
-  private Thread t;
-  private Container parent;
-
-  /**
-   * This Hashtable contains information retrievable by getParameter() in case
-   * the program is run as an application and the AppletStub is missing.
-   */
-  public Hashtable params;
-
-  /** 
-   * Retrieve the current version of the applet.
-   * @return String a string with the version information.
-   */
-  public String getAppletInfo()
-  {
-    String info = "The Java(tm) Telnet Applet\n$Id: telnet.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $\n";
-    info += "Terminal emulation: "+term.getTerminalType()+
-      " ["+term.toString()+"]\n";
-    info += "Terminal IO version: "+tio.toString()+"\n";
-    if(modules != null && modules.size() > 0) {
-      info += "Resident modules loaded: ("+modules.size()+")";
-      for(int i = 0; i < modules.size(); i++)
-        info += "   + "+(modules.elementAt(i)).toString()+"\n";
-    }
-    
-    return info;
-  }
-    
-  /**
-   * Retrieve parameter tag information. This includes the tag information from
-   * terminal and loaded modules.
-   * @return String an array of array of string with tag information
-   * @see java.applet.Applet#getParameterInfo
-   */
-  public String[][] getParameterInfo()
-  {
-    String pinfo[][];
-    String info[][] = {
-      {"address",  "String",   "IP address"},
-      {"port",     "Integer",  "Port number"},
-      {"proxy",    "String",   "IP address of relay"},
-      {"proxyport","Integer",  "Port number of relay"},
-      {"emulation","String",   "Emulation to be used (standard is vt320)"},
-      {"localecho","String",   "Localecho Mode (on/off/auto)"},
-    };
-    String tinfo[][] = (term != null ? term.getParameterInfo() : null);
-    if(tinfo != null) pinfo = new String[tinfo.length + 3][3];
-    else pinfo = new String[3][3];
-    System.arraycopy(info, 0, pinfo, 0, 3);
-    System.arraycopy(tinfo, 0, pinfo, 3, tinfo.length);
-    return pinfo;
-  }
-
-  /**
-   * We override the Applet method getParameter() to be able to handle 
-   * parameters even as application.
-   * @param name The name of the queried parameter.
-   * @return the value of the parameter
-   * @see java.applet.Applet#getParameter
-   */ 
-  public String getParameter(String name)
-  {
-    if(params == null) return super.getParameter(name);
-    return (String)params.get(name);
-  }
-
-  /**
-   * The main function is called on startup of the application.
-   */
-  public static void main(String args[]) 
-  {
-    // an application has to create a new instance of itself.
-    telnet applet = new telnet();
-
-    // create params from command line arguments
-    applet.params = new Hashtable();
-    switch(args.length) 
-    {
-    case 2: applet.params.put("port", args[1]);
-    case 1: applet.params.put("address", args[0]); 
-      break;
-    default: 
-      System.out.println("Usage: java telnet host [port]");
-      System.exit(0);
-    } 
-    applet.params.put("VTscrollbar", "true");
-    applet.params.put("module#1", "ButtonBar");
-    applet.params.put("1#Button", "Exit|\\$exit()");
-    applet.params.put("2#Button", "Connect|\\$connect(\\@address@,\\@port@)");
-    applet.params.put("3#Input", "address#30|"
-          +(args.length > 0 ? args[0] : "localhost"));
-    applet.params.put("4#Input", "port#4|23");
-    applet.params.put("5#Button", "Disconnect|\\$disconnect()");
-
-    // we put the applet in its own frame
-    Frame frame = new Frame("The Java Telnet Application ["+args[0]+"]");
-    frame.setLayout(new BorderLayout());
-    frame.add("Center", applet);
-    frame.resize(380, 590);
-
-    applet.init();
-
-    frame.pack();
-    frame.show();
-
-    applet.start();
-  }
-  
-  /**
-   * Initialize applet. This method reads the PARAM tags "address",
-   * "port" and "emulation". The emulation class is loaded dynamically.
-   * It also loads modules given as parameter "module#<nr>".
-   */
-  public void init()
-  {
-    String tmp; 
-
-    // save the current parent for future use
-    parent = getParent();
-    
-    // get the address we want to connect to
-    if((address = getParameter("address")) == null)
-      address = getDocumentBase().getHost();
-
-    if((tmp = getParameter("port")) == null) 
-      port = 23;
-    else
-      port = Integer.parseInt(tmp);
-
-    if((proxy = getParameter("proxy")) != null)
-      if((tmp = getParameter("proxyport")) == null)
-        proxyport = 31415;
-      else
-        proxyport = Integer.parseInt(tmp);
-    
-    if((emulation = getParameter("emulation")) == null)
-      emulation = "vt320";
-
-    // load the terminal emulation
-    try {
-      term = (Terminal)Class.forName("display."+emulation).newInstance();
-      System.out.println("telnet: load terminal emulation: "+emulation);
-    } catch(Exception e) {
-      System.err.println("telnet: cannot load terminal emulation "+emulation);
-      e.printStackTrace();
-    }
-    setLayout(new BorderLayout());
-    
-    // load modules, position is determined by the @<position> modifier
-    modules = new Vector();
-    int nr = 1;
-    while((tmp = getParameter("module#"+nr++)) != null) try {
-      Panel north = null, south = null, west = null, east = null;
-      String position = "North", initFile = null;
-
-      // try to get the initialization file name
-      if(tmp.indexOf(',') != -1) {
-        initFile = tmp.substring(tmp.indexOf(','+1));
-        tmp = tmp.substring(0, tmp.indexOf(','));
-        initFile = tmp.substring(tmp.indexOf(','+1));
-      }
-           
-      // find the desired location
-      if(tmp.indexOf('@') != -1) {
-        position = tmp.substring(tmp.indexOf('@')+1);
-        tmp = tmp.substring(0, tmp.indexOf('@'));
-      } 
-      Object obj = (Object)Class.forName("modules."+tmp).newInstance();
-
-      // probe for module (implementing modules.Module)
-      try {
-        ((Module)obj).setLoader(this);
-        modules.addElement((Module)obj);
-        System.out.println("telnet: module "+tmp+" detected");
-      } catch(ClassCastException e) {
-        System.out.println("telnet: warning: "+tmp+" may not be a "+
-                           "valid module");
-      }
-
-      // probe for visible component (java.awt.Component and descendants)
-      try {
-	Component component = (Component)obj;
-	if(position.equals("North")) {
-          if(north == null) { north = new Panel(); add("North", north); }
-          north.add(component);
-        } else if(position.equals("South")) {
-          if(south == null) { south = new Panel(); add("South", south); }
-          south.add(component);
-        } else if(position.equals("East")) {
-          if(east == null) { east = new Panel(); add("East", east); }
-          east.add(component);
-        } else if(position.equals("West")) {
-          if(west == null) { west = new Panel(); add("West", west); } 
-          west.add(component);
-        }
-        System.err.println("telnet: module "+tmp+" is a visible component");
-      } catch(ClassCastException e) {}
-
-    } catch(Exception e) {
-      System.err.println("telnet: cannot load module "+tmp);
-      e.printStackTrace();
-    }
-    if(modules.isEmpty()) modules = null;
-    add("Center", term);
-  }
-
-  /**
-   * Upon start of the applet try to create a new connection.
-   */
-  public void start()
-  {
-    if(!connect(address, port) && params == null)
-      showStatus("telnet: connection to "+address+" "+port+" failed");
-  }
-
-  /**
-   * Disconnect when the applet is stopped.
-   */
-  public final void stop()
-  {
-    disconnect();
-  }
-
-  /**
-   * Try to read data from the sockets and put it on the terminal.
-   * This is done until the thread dies or an error occurs.
-   */
-  public void run()
-  {
-    while(t != null)
-      try {
-        String tmp = new String(tio.receive(), 0);
-
-        // cycle through the list of modules
-        if(modules != null) {
-          Enumeration modlist = modules.elements();
-          while(modlist.hasMoreElements()) {
-            Module m = (Module)modlist.nextElement();
-            String modified = m.receive(tmp);
-            // call the receive() method and if it returns null
-            // remove the module from the list
-            if(modified == null) modules.removeElement(m);
-            else tmp = modified;
-          }
-        }
-        // put the modified string to the terminal
-        term.putString(tmp);
-    } catch(IOException e) {
-      disconnect();
-    }
-  }
-
-  /**
-   * Connect to the specified host and port but don't break existing 
-   * connections. Connects to the host and port specified in the tags. 
-   * @return false if connection was unsuccessful
-   */
-  public boolean connect()
-  {
-    return connect(address, port);
-  }
-
-  /**
-   * Connect to the specified host and port but don't break existing 
-   * connections. Uses the port specified in the tags or 23.
-   * @param host destination host address
-   */
-  public boolean connect(String host)
-  {
-    return connect(host, port);
-  }
-  
-  /**
-   * Connect to the specified host and port but don't break existing 
-   * connections.
-   * @param host destination host address
-   * @param prt destination hosts port
-   */
-  public boolean connect(String host, int prt)
-  {
-    address = host; port = prt;
-
-    if(address == null || address.length() == 0) return false;
-    
-    // There should be no thread when we try to connect
-    if(t != null && connected) {
-      System.err.println("telnet: connect: existing connection preserved");
-      return false;
-    } else t = null;
-    
-    try {
-      // In any case try to disconnect if tio is still active
-      // if there was no tio create a new one.
-      if(tio != null) try { tio.disconnect(); } catch(IOException e) {}
-      else (tio = new TelnetIO()).setPeer(this);
-
-      term.putString("Trying "+address+(port==23?"":" "+port)+" ...\n\r");
-      try {
-        // connect to to our destination at the given port
-        if(proxy != null) {
-          tio.connect(proxy, proxyport);
-          String str = "relay "+address+" "+port+"\n";
-          byte[] bytes = new byte[str.length()];
-          str.getBytes(0, str.length(), bytes, 0);
-          tio.send(bytes);
-        } else 
-          tio.connect(address, port);
-        term.putString("Connected to "+address+".\n\r");
-        // initial conditions are connected and localecho
-        connected = true;
-        localecho = true;
-	if ( (getParameter("localecho")!=null) && 
-	     getParameter("localecho").equals("no")
-        )
-	  localecho = false;
-
-        // cycle through the list of modules and notify connection
-        if(modules != null) {
-          Enumeration modlist = modules.elements();
-          while(modlist.hasMoreElements())
-            // call the connect() method
-            ((Module)modlist.nextElement()).connect(address, port);
-        }
-      } catch(IOException e) {
-        term.putString("Failed to connect.\n\r");
-        // to be sure, we remove the TelnetIO instance
-        tio = null;
-        System.err.println("telnet: failed to connect to "+address+" "+port);
-        e.printStackTrace();
-        return false;
-      }
-      // if our connection was successful, create a new thread and start it
-      t = new Thread(this);
-      t.setPriority(Thread.MIN_PRIORITY);
-      t.start();
-    } catch(Exception e) {
-      // hmm, what happened?
-      System.err.println("telnet: an error occured:");
-      e.printStackTrace();
-      return false;
-    }
-    return true;
-  }
-
-  /**
-   * Disconnect from the remote host.
-   * @return false if there was a problem disconnecting.
-   */
-  public boolean disconnect()
-  {
-    if(tio == null) {
-      System.err.println("telnet: no connection");
-      return false;
-    }
-    try {
-      connected = false; t = null;
-      // cycle through the list of modules and notify connection
-      if(modules != null) {
-        Enumeration modlist = modules.elements();
-        while(modlist.hasMoreElements())
-          // call the disconnect() method
-          ((Module)modlist.nextElement()).disconnect();
-      }
-      term.putString("\n\rConnection closed.\n\r");
-      tio.disconnect();
-    } catch(Exception e) {
-      System.err.println("telnet: disconnection problem");
-      e.printStackTrace();
-      tio = null; t = null;
-      return false;
-    }
-    return true;
-  }
-  
-  /**
-   * Send a String to the remote host. Implements display.TerminalHost
-   * @param s String to be sent
-   * @return true if we are connected
-   * @see display.TerminalHost
-   */
-  public boolean send(String str)
-  {
-    if(connected) try {
-      byte[] bytes = new byte[str.length()];
-      str.getBytes(0, str.length(), bytes, 0);
-      tio.send(bytes);
-      if(localecho) {
-        if ((str.length()==2) && (str.charAt(0)=='\r') && (str.charAt(1)==0))
-          term.putString("\r\n");
-        else
-          term.putString(str);
-      }
-    } catch(Exception e) {
-      System.err.println("telnet.send(): disconnected");
-      disconnect();
-      return false;
-    }
-    else return false;
-    return true;
-  }
-
-  /**
-   * Send a String to the remote Host.
-   * @param str String to be sent
-   * @return true if we are connected
-   * @see modules.BSXModule
-   */
-  public boolean writeToSocket(String str)
-    {
-      if (connected) try {
-	byte[] bytes = new byte[str.length()];
-	str.getBytes(0, str.length(), bytes, 0);
-	tio.send(bytes);
-      } catch(Exception e) {
-	System.err.println("telnet.send(): disconnected");
-	disconnect();
-	return false;
-      }
-      else return false;
-      return true;
-    }
-  /**
-   * Send a String to the users terminal
-   * @param str String to be displayed
-   * @return void
-   * @see modules.BSXModule
-   */
-  public void writeToUser(String str)
-  {
-    if (term!=null)
-      term.putString(str);
-  }
-
-  /**
-   * This method is called when telnet needs to be notified of status changes.
-   * @param status Vector of status information.
-   * @return an object of the information requested.
-   * @see socket.StatusPeer
-   */
-  public Object notifyStatus(Vector status)
-  {
-    String what = (String)status.elementAt(0);
-
-    if(what.equals("NAWS"))
-      return term.getSize();
-    if(what.equals("TTYPE"))
-      if(term.getTerminalType() == null)
-        return emulation;
-      else return term.getTerminalType();
-
-    if(what.equals("LOCALECHO") || what.equals("NOLOCALECHO")) {
-      boolean nlocalecho = localecho ;
-    if(what.equals("LOCALECHO"))
-	nlocalecho = true;
-    if(what.equals("NOLOCALECHO")) 
-	nlocalecho = false;
-      if ( (getParameter("localecho")==null) ||
-	   getParameter("localecho").equals("auto")
-      )
-	localecho = nlocalecho;
-      /* else ignore ... */
-    }
-    return null;
-  }
-}
diff --git a/web/root/telnet/tools/Makefile b/web/root/telnet/tools/Makefile
deleted file mode 100644
index 860d4590dc..0000000000
--- a/web/root/telnet/tools/Makefile
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# This file is part of "The Java Telnet Applet".
-#
-# This 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, or (at your option)
-# any later version.
-#
-# "The Java Telnet Applet" 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 The JAVA Telnet Applet; see the file COPYING.  If not, write to 
-# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-OPT	= -O
-DEBUG	= -g
-
-.SUFFIXES: .java .class .html
-
-.java.class:
-	javac -classpath ../:$(CLASSPATH) $(OPT) $(DEBUG) $<
-
-.class.html:	
-	(env CLASSPATH=../:$(CLASSPATH); appletviewer $@)
-
-all: proxy.class 
-
-relay: mrelayd relayd
-
-clean:	
-	rm -f *~ 
-
-realclean:	clean
-	rm -f *.class relayd mrelayd
-
-# dependencies
-
-proxy.class: proxy.java
-redirector.class: proxy.java
diff --git a/web/root/telnet/tools/mrelayd b/web/root/telnet/tools/mrelayd
deleted file mode 100644
index 9d3ce804bf52195ac9273fd5d06fce36037e41f8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 16535
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
RdyCAEN8K<C*(Qvv000y%FXaFL

diff --git a/web/root/telnet/tools/mrelayd.c b/web/root/telnet/tools/mrelayd.c
deleted file mode 100644
index 27c79aae48..0000000000
--- a/web/root/telnet/tools/mrelayd.c
+++ /dev/null
@@ -1,566 +0,0 @@
-/**
- * mrelayd.c -- a relay daemon
- * --
- * $Id: mrelayd.c,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/* mrelayd.c (c) 1996,1997 Marcus Meissner <marcus@mud.de> */
-
-/* Compile with: cc -o relayd relayd.c
- *           or: gcc -o relayd relayd.c
- * Solaris: (g)cc -o relayd relayd.c -lsocket -lnsl
- */
-
-/* this program expects the string:
- * "relay <hostname> <port>" or "relay <hostname>"
- * after connecting. It will then try to connect to the specified host
- * if failures occur, it will terminate the connection.
- */
-
-/* adjust this to a reasonable limit */
-#define MAXUSERS  120
-
-/* message printed if all slots are used ... */
-#define FAILMESSAGE "Sorry, all slots are full.\r\n"
-
-/* string printed before connection */
-#define RELAYHEADER "Relayd $Revision: 1.1.1.1 $ (c) Marcus Meissner\r\n"
-
-/* the tcp port this demons is listening on ... */
-#define LISTENPORT  31415
-
-/* default connect port (telnet) */
-#define DEFAULTPORT 23
-
-/* default buffersize */
-#define DEFAULTSIZE	2000
-
-#include <stdio.h>
-#ifdef _WIN32
-#include <winsock.h>
-#include <signal.h>
-#define ioctl ioctlsocket
-#define read(a,b,c) recv(a,b,c,0)
-#define write(a,b,c) send(a,b,c,0)
-#define close _lclose
-#define EINTR WSAEINTR
-#define perror xperror
-void xperror(char *s) {
-	fprintf(stderr,"%s: %d\n",s,GetLastError());
-}
-#else
-#include <sys/time.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/errno.h>
-#include <sys/signal.h>
-#include <sys/fcntl.h>
-#include <sys/ioctl.h>
-#endif
-#include <memory.h>
-#include <malloc.h>
-#include <string.h>
-#include <sys/types.h>
-
-#if defined(sun) && defined(__GNUC__)
-int socket(int,int,int);
-int shutdown(int,int);
-int close(int);
-int bind(int,struct sockaddr*,int);
-int listen(int,int);
-void bzero(char*,int);
-int select(int,fd_set *,fd_set*,fd_set*,struct timeval*);
-int accept(int,struct sockaddr*,int*);
-int connect(int,struct sockaddr*,int);
-int recvfrom(int,char*,int,int,struct sockaddr*,int*);
-/*void perror(char*); SLOWLARIS HASS*/
-/*int sendto(int,char*,int,int,struct sockaddr*,int); SLOWLARIS HASS*/
-#endif
-
-#ifdef hpux
-/* redefinition... to avoid prototype in <time.h> */
-#define FD_CAST int
-#endif
-
-#ifdef sgi
-void bzero(void*,int);
-#endif
-
-#ifndef FD_CAST
-#define FD_CAST fd_set
-#endif
-
-extern int errno;
-
-struct relay {
-	char	*inbuf,*outbuf;
-	int	infd,outfd,incur,outcur,insize,outsize;
-	struct	sockaddr_in	inaddr,outaddr;
-	int	state;
-#define STATE_ACCEPTED		0
-#define STATE_OK		1
-	int	flags;
-#define FLAG_EOF_USER		1
-#define FLAG_EOF_TARGET		2
-#define FLAG_CLOSED_TARGET	4
-#define FLAG_CLOSED_USER	8
-} *relays = NULL;
-int nrofrelays = 0;
-
-void*
-xmalloc(int size) {
-	void*x;
-
-	x=malloc(size);
-	if (!x && size) {
-		fprintf(stderr,"Out of memory, exiting.\n");
-		exit(1);
-	}
-	return x;
-}
-
-void*
-xrealloc(void *y,int size) {
-	void*x;
-
-	x=realloc(y,size);
-	if (!x && size) {
-		fprintf(stderr,"Out of memory, exiting.\n");
-		exit(1);
-	}
-	return x;
-}
-
-static int
-fd_make_nonblocking(int fd) {
-  int     isnonblock=0;
-
-#ifdef FIONBIO
-  if (!isnonblock) {
-    int     b;
-    b=1;
-    if (-1==ioctl(fd,FIONBIO,&b)) {
-      perror("ioctl FIONBIO");
-    } else
-      isnonblock=1;
-  }
-#endif
-#ifdef O_NDELAY
-  if (!isnonblock) {
-    int     flags;
-
-    if (-1==(flags=fcntl(fd,F_GETFL))) {
-      perror("fcntl F_GETFL");
-    } else {
-      flags|=O_NDELAY;
-      if (-1==fcntl(fd,F_SETFL,flags)) {
-        perror("fcntl F_SETFL  O_NDELAY");
-      } else
-        isnonblock=1;
-    }
-  }
-#endif
-#ifdef O_NONBLOCK
-  if (!isnonblock) {
-    int     flags;
-
-    if (-1==(flags=fcntl(fd,F_GETFL))) {
-      perror("fcntl F_GETFL");
-    } else {
-      flags|=O_NONBLOCK;
-      if (-1==fcntl(fd,F_SETFL,flags)) {
-        perror("fcntl F_SETFL  O_NONBLOCK");
-      } else
-        isnonblock=1;
-    }
-  }
-#endif
-  return isnonblock;
-}
-
-void
-clean_connection(struct relay *relay) {
-  if (!relay) return;
-  if (relay->outfd>=0) {
-    if (-1==close(relay->outfd))
-      perror("close");
-    relay->outfd=-1;
-  }
-  if (relay->infd>=0) {
-    if (-1==close(relay->infd))
-      perror("close");
-    relay->infd=-1;
-  }
-  free(relay->outbuf);free(relay->inbuf);
-  memcpy(relay,relay+1,sizeof(struct relay)*(nrofrelays-(relay-relays)-1));
-  relays = xrealloc(relays,sizeof(struct relay)*(--nrofrelays));
-}
-
-void
-main(argc,argv)
-int argc;
-char  **argv;
-{
-  int i,j,res;
-  int acfd;
-  struct  sockaddr_in acsa;
-  char	readbuf[1000],relaystring[1000];
-  struct  in_addr targetaddr;
-#ifdef _WIN32 
-  {
-	WSADATA wsad;
-	
-	WSAStartup(0x0101,&wsad);
-  }
-#else
-  close(0);
-  close(1);
-#endif
-#ifdef SIGPIPE
-  signal(SIGPIPE,SIG_IGN);
-#endif
-#ifdef SIGHUP
-  signal(SIGHUP,SIG_IGN); /* don't terminate on session detach */
-#endif
-  strcpy(relaystring,FAILMESSAGE);
-  if (-1==(acfd=socket(PF_INET,SOCK_STREAM,0))) {
-    perror("socket(accept_socket)");
-    exit(1);
-  }
-
-  acsa.sin_family=AF_INET;
-  acsa.sin_port=htons(LISTENPORT);
-  acsa.sin_addr.s_addr=INADDR_ANY;
-#ifdef SO_REUSEADDR
-  {
-    int reuseit=1;
-    if (-1==setsockopt(acfd,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseit,sizeof(reuseit)))
-	perror("setsockopt SOL_SOCKET SO_REUSEADDR");
-  }
-#endif
-  if (-1==bind(acfd,(struct sockaddr*)&acsa,sizeof(struct sockaddr_in))) {
-    perror("bind");
-    exit(1);
-  }
-  /* 5 is usual the maximum anyway */
-  if (-1==listen(acfd,5)) {
-    perror("listen");
-    exit(1);
-  }
-  while (1) {
-    fd_set  readfds,writefds;
-    int width;
-
-    width=3;
-    if (acfd>=width)
-      width=acfd+1;
-restart_select:
-    FD_ZERO(&readfds);FD_ZERO(&writefds);
-    FD_SET(acfd,&readfds);
-    for (i=nrofrelays;i--;) {
-      struct relay *relay = relays+i;
-
-      /* both sides closed? -> clean */
-      if ((relay->flags & (FLAG_CLOSED_TARGET|FLAG_CLOSED_USER)) ==
-      	(FLAG_CLOSED_TARGET|FLAG_CLOSED_USER)
-      ) {
-      	clean_connection(relay);
-	continue;
-      }
-      /* transmitted all stuff left to user? -> close */
-      if ((relay->flags&(FLAG_CLOSED_TARGET|FLAG_EOF_TARGET))&&(!relay->outcur)) {
-	clean_connection(relay);
-	continue;
-      }
-      /* transmitted all stuff left to target? -> close */
-      if ((relay->flags&(FLAG_CLOSED_USER|FLAG_EOF_USER))&&(!relay->incur)) {
-	clean_connection(relay);
-	continue;
-      }
-
-      if (relay->outfd>=0) {
-        /*need to do that... else it will cause load 1*/
-        if (relay->incur)
-          FD_SET(relay->outfd,&writefds);
-	if (!(relay->flags & FLAG_EOF_TARGET))
-	    FD_SET(relay->outfd,&readfds);
-        if (relay->outfd>=width)
-          width=relay->outfd+1;
-      }
-      if (relay->infd>=0) {
-        /*need to do that... else it will cause load 1*/
-        if (relay->outcur)
-          FD_SET(relay->infd,&writefds);
-	if (!(relay->flags & FLAG_EOF_USER))
-          FD_SET(relay->infd,&readfds);
-        if (relay->infd>=width)
-          width=relay->infd+1;
-      }
-    }
-    if (-1==select(   width,
-          (FD_CAST*)&readfds,
-          (FD_CAST*)&writefds,
-          NULL,/*no exceptfds.*/
-          0)
-    ) {
-      if (errno!=EINTR)
-        perror("select");
-      else
-        goto  restart_select;
-    }
-    if (FD_ISSET(acfd,&readfds)) {
-      int afd;
-      int aclen;
-      struct  sockaddr_in conaddr;
-      struct relay *relay = NULL;
-
-      aclen=sizeof(struct sockaddr_in);
-      if (-1==(afd=accept(acfd,(struct sockaddr*)&conaddr,&aclen)))
-        perror("accept");
-      if (relays)
-	  relays=(struct relay*)xrealloc(relays,sizeof(struct relay)*(nrofrelays+1));
-      else
-	  relays=(struct relay*)xmalloc(sizeof(struct relay));
-      nrofrelays++;
-      relay = relays+(nrofrelays-1);
-      relay->inbuf	= xmalloc(DEFAULTSIZE);
-      relay->outbuf	= xmalloc(DEFAULTSIZE);
-      relay->insize	= DEFAULTSIZE;
-      relay->outsize	= DEFAULTSIZE;
-      relay->flags	= 0;
-      relay->incur	= 0;
-      relay->outcur	= 0;
-      relay->infd	= afd;
-      relay->outfd	= -1;
-      relay->state	= STATE_ACCEPTED;
-      memcpy(&relay->inaddr,&conaddr,sizeof(struct sockaddr_in));
-      if (nrofrelays>=MAXUSERS) {
-        strcpy(relay->outbuf,relaystring);
-	relay->outcur = strlen(relaystring)+1;
-	relay->state = STATE_OK;
-	relay->flags = FLAG_CLOSED_TARGET;
-      }
-#ifdef SO_LINGER
-      {
-	struct linger sol;
-	sol.l_linger = 5;
-	sol.l_onoff = 1;
-	if (-1==setsockopt(acfd,SOL_SOCKET,SO_LINGER,(char*)&sol,sizeof(sol)))
-	    perror("setsockopt SOL_SOCKET SO_LINGER");
-      }
-#endif
-    }
-    for (i=nrofrelays;i--;) {
-      struct relay *relay = relays+i;
-
-      if ((relay->infd>=0) && FD_ISSET(relay->infd,&readfds)) {
-        do {
-          if (-1==(res=read(relay->infd,readbuf,1000))) {
-            if (errno==EINTR)
-              break;
-            /* user side has broken the connection */
-	    close(relay->infd);relay->infd=-1;
-	    relay->flags |= FLAG_CLOSED_USER;
-            break;
-          }
-          break;
-        } while (1);
-        if (res==0) {
-	  /* we read the End Of File marker. but we still have to write 
-	   * the rest of the text
-	   */
-	  relay->flags |= FLAG_EOF_USER;
-        }
-        if (res>0) {
-          readbuf[res]='\0';
-          while (relay->incur+res>=relay->insize) {
-            relay->inbuf=xrealloc(relay->inbuf,relay->insize*2);
-            relay->insize*=2;
-          }
-          memcpy(relay->inbuf+relay->incur,readbuf,res+1);
-          relay->incur+=res;
-        }
-        if (	(relay->outfd==-1) &&
-		(relay->state==STATE_ACCEPTED) &&
-		memchr(relay->inbuf,'\n',relay->incur)
-        ) {
-          char  sendbuf[200];
-          struct  hostent *hp;
-          char  *s,*nextchar,*tmp;
-          int port;
-
-          s = memchr(relay->inbuf,'\n',relay->incur);
-          if (!s)
-            continue;
-          *s='\0';
-          nextchar=s+1;
-          if ((s=memchr(relay->inbuf,'\r',(s-relay->inbuf))))
-            *s='\0';
-
-
-	  relay->state = STATE_OK;
-
-          tmp = (char*)xmalloc(strlen(relay->inbuf));
-          if (2!=sscanf(relay->inbuf,"relay %s %d",
-            tmp,&port
-          )) {
-            if (!sscanf(relay->inbuf,"relay %s",tmp)) {
-
-              free(tmp);
-              /* we avoid telling potential hackers how to use this relay */
-	      sprintf(relay->outbuf,"550 Bad syntax. Go away.\n",tmp);
-	      relay->outcur = strlen(relay->outbuf);
-	      relay->flags = FLAG_CLOSED_TARGET;
-              continue;
-            } else
-              port = DEFAULTPORT;
-          }
-          hp=gethostbyname(tmp);
-          if (!hp) {/* not found */
-	    sprintf(relay->outbuf,"No hostentry for '%s'!\n",tmp);
-            free(tmp);
-	    relay->outcur = strlen(relay->outbuf);
-	    relay->flags = FLAG_CLOSED_TARGET;
-            continue;
-          }
-          memcpy(&targetaddr,hp->h_addr_list[0],sizeof(struct in_addr));
-          relay->outaddr.sin_family=AF_INET;
-          relay->outaddr.sin_port=htons(port);
-          memcpy(&(relay->outaddr.sin_addr),&targetaddr,4);
-          strcpy(sendbuf,RELAYHEADER);
-          relay->outcur=strlen(sendbuf);
-          memcpy(relay->outbuf,sendbuf,strlen(sendbuf)+1);
-          if (-1==(relay->outfd=socket(PF_INET,SOCK_STREAM,0)))
-            perror("socket(connect_socket)");
-#ifndef _WIN32
-          (void)fd_make_nonblocking(relay->outfd);
-#endif
-          if (  (-1==connect( relay->outfd,
-              (struct sockaddr*)&(relay->outaddr),
-              sizeof(struct sockaddr_in))
-#ifdef _WIN32
-				) && (WSAGetLastError()!=WSAEINPROGRESS)
-#else
-				) && (errno!=EINPROGRESS)
-#endif
-          ) {
-            sprintf(readbuf,"Connect to %s failed: %s\n",tmp,strerror(errno));
-            perror("connect");
-	    close(relay->outfd);relay->outfd=-1;
-	    relay->state = STATE_OK;
-	    relay->flags |= FLAG_CLOSED_TARGET;
-	    strcpy(relay->outbuf,readbuf);
-	    relay->outsize = strlen(readbuf)+1;
-            free(tmp);
-            continue;
-          }
-          free(tmp);
-#ifdef SEND_REMOTEIP
-          /* only useful if you want to tell the
-           * remotemud the _real_ host the caller
-           * is calling from
-           */
-          tmphp=gethostbyaddr(
-            (char*)(&(conaddr.sin_addr)),
-            sizeof(struct in_addr),
-            AF_INET
-          );
-          if (!tmphp) {
-            sprintf(sendbuf,"remoteip %s %s\n",
-              inet_ntoa(conaddr.sin_addr),
-              inet_ntoa(conaddr.sin_addr)
-            );
-          } else {
-            sprintf(sendbuf,"remoteip %s %s\n",
-              inet_ntoa(conaddr.sin_addr),
-              tmphp->h_name
-            );
-          }
-          memcpy(relay->inbuf,sendbuf,strlen(sendbuf)+1);
-          relay->incur=strlen(sendbuf);
-#else
-          relay->inbuf[0]='\0';
-          relay->incur=0;
-#endif
-        }
-      }
-      if ((relay->outfd>=0) && FD_ISSET(relay->outfd,&readfds)) {
-        do {
-          if (-1==(res=read(relay->outfd,readbuf,1000))) {
-            if (errno==EINTR)
-              continue;
-            /* the mudside has broken the
-             * connection. we still have
-             * to transmit the rest of
-             * the text
-             */
-            close(relay->outfd);relay->outfd=-1;
-	    relay->flags |= FLAG_CLOSED_TARGET;
-            break;
-          }
-          break;
-        } while (1);
-        if (res==0) {
-	  /* we read the End Of File marker. but we still have to write 
-	   * the rest of the text
-	   */
-	  relay->flags |= FLAG_EOF_TARGET;
-	}
-        if (res>0) {
-          /* 0 is not automagically appended. */
-          readbuf[res]='\0';
-          while (relay->outcur+res>=relay->outsize) {
-            relay->outbuf=xrealloc(relay->outbuf,relay->outsize*2);
-            relay->outsize*=2;
-          }
-          memcpy(relay->outbuf+relay->outcur,readbuf,res+1);
-          relay->outcur+=res;
-        }
-      }
-      if ((relay->infd>=0) && FD_ISSET(relay->infd,&writefds)) {
-        j=relay->outcur;
-        if (-1==(res=write(relay->infd,relay->outbuf,j))) {
-          if (errno!=EINTR) {
-	    close(relay->infd);relay->infd=-1;
-	    relay->flags |= FLAG_CLOSED_USER;
-          }
-        }
-        if (res>0) {
-          memcpy(relay->outbuf,relay->outbuf+res,relay->outcur-res);
-          relay->outcur-=res;
-        }
-      }
-      if ((relay->outfd>=0) && FD_ISSET(relay->outfd,&writefds)) {
-        j=relay->incur;
-        if (-1==(res=write(relay->outfd,relay->inbuf,j))) {
-          if (errno!=EINTR) {
-            close(relay->outfd);relay->outfd=-1;
-	    relay->flags |= FLAG_CLOSED_TARGET;
-          }
-        }
-        if (res>0) {
-          memcpy(relay->inbuf,relay->inbuf+res,relay->incur-res);
-          relay->incur-=res;
-        }
-      }
-    }
-  }
-}
diff --git a/web/root/telnet/tools/mrelayd.exe b/web/root/telnet/tools/mrelayd.exe
deleted file mode 100755
index d33f2d3cc6f8cab216b6f0feb0d1f26b8fb9de6e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 39936
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

diff --git a/web/root/telnet/tools/proxy.class b/web/root/telnet/tools/proxy.class
deleted file mode 100644
index c64e0eba0f001753af79ad00ba71688834e86782..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3592
zcmX^0Z`VEsW(Hk`3mgoo91Lk34Cx#U9UKhZ91Oi23==sRrf@K<;b7R!!LWyeVJ`>6
zJ`RTc91I6I7!I;C%wcDk%g!*5onbyZ!vc1Oh3pKA*cleHGb~|eSjx_@jGbXQJHrZg
zhL!9LtJoQKvNIgzVi03+;bM>n5wZ-fTnq{fZd?q44DMVEQVbqk3=#~UTnusyUR(@{
z4BlJ}vJ5^TF<%hj2O|7IvH>93Ku!iph9E8mNrqr91{sDBkVGhm2;*c>U<d~h5g_9u
zLF%Ho7(^MOxfrAwVz?ND8Dc@QaUdcdL?nPLPXrN3AUl#l4ocx+sA9+g5qTgYA4C*@
zh$0YC3L?rtL^%&b4?{K&Lmxvfh$sXRB|HohKzb)LRPZoNV`$)En9h*L!!Uy(lZRm@
zLlzIiEQSmohSdyBJPd0YYIqpdGnDc$Yz3*?#!$?|upOj#2T1G~Ln9BvagdTz49z?Y
zr$MHi0m+^N8Fikal#zjfk%3jgM%PY(k%3bou|%ODzo<lkkwHR3(<dvjEKxr*U*Es9
zq@c7UxTGjGG1r=rK?Ek1lbDyTA6!zDnU`*jq$4l2MBg(nwZt(cr6{$y*qV_+7^VXx
z6`Y@(omyhe$iS?j8OF#Ut>Fo^3CTF8(zLYHB5Otl77b63B5@5*h-;A4Sc7FGH6Rk`
z&IIYwfy#k3`6p$iCYM;_u}d6Tc~EIyUSd*Csx`<UQDhmYhc!JJ8Bi>Mi9_52wi6-*
z_Y78J!WbFEuow^aoGMfTDcB&UBME@q$FJd2l$w%Rl$u<UUj%lsm_{^2C6YV95^Oe^
zd6^}4j0`N!`6;Q442+(P4E#Qsd8vM-xk;%-As|09GDtx*XXfj>B$gzi#lI+A($n9y
zB0067Br`wHnvnq^9#E8-he$6%5cS9|VPrrS49O@;O-!+7WDtj_1p6+vs4TSzo>l~r
zWMJt>$R|HJF()jsC==vpsLR=X3X1Y8Dy<nAc#y+{kwGCSH8;N`RUsq4xJ02SHLbKb
zHANvgKQAve8RULFE=C5f;QZ2}<W#rJoK!{zVJrd2$iNh<z{tRsm<;kYBZDM_fntzC
zT2X#3SW!ViYF-K>15092dNCsdYjRG0aVjGNH_XnwbOlBRE*LLAkCA~bIlr_3WVc94
zYH>+sULwe05C>-#D=;z$A_+r6hLM3ewWx@ZfhDyfvxJd>J3Y0;Bfq%BFEKZjk%2Eg
zwFD_2GBWU|r<NecB_jiKW_~Iog9I$2qenI)gD^}ADXNh~ks=x<3XW!YvS4Ho1e*XB
z^~@_tO;0UiWDr1zL9!(ygAhUpmh~7J#1MkWSqa5};L75X)LaxHNP0qXP)J5memN+a
zFfxdM4F#n!l)w>%3kM@5Jw^t8gg7|iF*5LhMM_IDb3nNxm63rXC$qRDH4o%7AqX=w
zFI^!&4_20P=717vKz<P@iok4m?B(RAGcvH|fC^nk2A15!%sfU0CQ#%t=N6|kGBD*A
zGcqvemzFRxa1<mK6{mXUl`t~!7eMk?Vsds!QDSl`BLh2FBqxuNffbzm85y|1v>wQ*
zAibbGVWkkw$e;!mw^B$gD#|aiQh+32q<o-|mYA87n!?DSiK;L;F)uH_L?O8-HL)aB
zp%@%Q3dPV6QO2tltf-ig!33(RAh9ShH?<_SNWlgvL4yO!&PpK<Tn;IuRurTrmw+6i
zfvF7?T-cO4L6w#kC#I)bDS&wjHel7Lc7X~3JB4UW`(qgyn2HQRgb^bHYf)lKN)aOi
z7ou<lb3o+?D4W4Kkf>!X0@=jKz+6-c%FK{3Vr1YhPAv&2%FHjyEU9E<U@b051f>I(
zl8nq^Mh1?Od`QY>WZ)_<$}CBB&PXgOW>92MU=U+qWDsXyV_;x7#?Z(B0&5v+KoqD#
z2h*z=n!xlVD1C~dnSp_Ug@KWwiXoqYk)fJ_fsuoOg@J{EfuTifJA;yz^i~FQEuF0l
zwpy}V8SJ!nGe|@VacpPM(AvUayNy9lh<O`>p4JuyyKM|Q+Zdd-GgxTtW{{24+QMMI
zok3+QgZ**_p72c!hC)2s7;GTAIU%~uk#$H#YHepw-^yU7wS~cqWgCM7h~CEFxRHU8
z!HmI`!Gl4Bfr+7(fq}t_fscWK!H9u{!GwX6!IVLm!JNU2!Gghz!IHt1!Gghq!HU6?
z!I~kE!ImL|!JeUn!GWQM!I7bv!HA)ifq_8_;x~q~3~k_$XkpmEP{Y6o@-M>?hDHV^
zFzW<E4FfX+6R4R42^EGo1{ScK+9n1|a3FAPXVBQqARoD%L3ta4t`O@s20iU<40_ub
zoVGF8Y-C`7x|a*AYchi>!$h$CVqm@++<rBN%?ynUj0~I%8yPAgb}?!&urjbRFfe2Z
zG5RpuGTUkGV_n3+4+=$rZ45RnI~a_#n2mNYSlKRT5C8`*KTMd#$VY1*>k<Z8Emn{M
zyX6eRU<G_o1<M(jcQYtN%4o~ZV&D)EU(UcHyPZMRie)1M6N4m!0)sMx2!j=aBZCV(
zyptFt7#J9Q7?>G+8Q2;88F(228AKU^86+7(86+9P7!(*n8I&2q8B`b|7<3pS84MVr
z8B7>r7_1oL7_1oL85|kn7+e?<7+e_=8GIR%83GyN7(y7*86p`n7*ZJ088R8Xz~N#7
z@i9X`Ll*-B12aQ9I1Mv0urj2ATC5CA4D1Za;B?H)kjcQvu!*4!oNiAt<Um7h1p^D%
zj9rXAT3Z<Gw=rn@EN9>cd!Kt7gU#~)uOR7<Z99X8_BIBSZ4Abc_}b23v712-lxA%~
zDS11CrY}S#JBmsnrfm#*Iv5%iBeiw5GidqkW>AdO(caFWWyPW^$+C^XK$1mw8-u~}
z|HWV@@NQ$U+07srslAQCbUTBZuZ|GQHU?+yZ4CO${|EZ&Y-6xq{@)Xv=C~Mm8AKS2
z8G0EeFo-bBWLVFzi9raOn#vet85kH!7?>DJ8JHN#7`PZp8F(4W8Tc3~7{nMV86+91
z8I&1n7<3t`8H^cf8H^d~7@QgE8Dbb37}6N(8FCmJ8Oj)%7#bOx8QK|I7<w6M8745a
zGE8J>W0=X%&M=dqgJCH{C&OxnE{63C?F^e3x*4`Jw1Z>FgMpoafngUk<uWnsU|7e{
z!obMj%;3u~m7xXf9w~-H4DAfe42=x@42K!o8CV#WGVn5NVW?wZV%W^U&M=#yo`H#h
Lk)acuKXw5C0m-0=

diff --git a/web/root/telnet/tools/proxy.java b/web/root/telnet/tools/proxy.java
deleted file mode 100644
index 564b5287b8..0000000000
--- a/web/root/telnet/tools/proxy.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/**
- * proxy -- a proxy server for telnet
- * --
- * $Id: proxy.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- * $timestamp: Tue May 27 15:08:19 1997 by Matthias L. Jugel :$
- *
- * This file is part of "The Java Telnet Applet".
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-import java.net.*;
-import java.io.*;
-import java.util.*;
-
-/**
- * proxy class -- implements a proxy server to redirect network access
- * @author Matthias L. Jugel, Marcus Mei�ner
- * @version $Id: proxy.java,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- */
-
-public class proxy implements Runnable
-{
-  String remoteHost;
-  int localPort, remotePort;
-
-  Thread listener, connection;
-
-  ServerSocket server;
-
-  /**
-   * Create a server socket and start listening on the local port.
-   * @param lport local port
-   * @param raddr address of the destination
-   * @param rport port on the destination host
-   */
-  public proxy(int lport, String raddr, int rport)
-  {
-    localPort = lport;
-    remoteHost = raddr; remotePort = rport;
-    
-    log("destination host is "+remoteHost+" at port "+remotePort);
-    try {
-      server = new ServerSocket(localPort);
-    } catch(Exception e) {
-      System.err.println("proxy: error: cannot create server socket");
-    }
-    log("listening on port "+localPort);
-
-    listener = new Thread(this);
-    listener.setPriority(Thread.MIN_PRIORITY);
-    listener.start();
-  }
-  
-  /**
-   * This method is called when the application is run on the commandline.
-   * It takes two or three arguments:
-   * usage: java proxy local-port destination-host destination-port
-   * @param args The command line arguments
-   */
-  public static void main(String args[]) 
-  {
-    String remoteHost = "";
-    int localPort = 0, remotePort = 0;
-    
-    if(args.length < 2)
-    {
-      System.err.println("proxy: usage: proxy <port> "+
-                         "<destination host> [<destination port>]");
-      System.exit(1);
-    }
-    try {
-      localPort = Integer.parseInt(args[0]);
-    } catch(Exception e) {
-      System.err.println("proxy: parameter <port>: number expected");
-      System.exit(1);
-    }
-    remoteHost = args[1];
-    if(args.length > 2)
-    {
-      try {
-        remotePort = Integer.parseInt(args[2]);
-      } catch(Exception e) {
-        System.err.println("proxy: parameter <destination port>: "+
-                           "number expected");
-        System.exit(1);
-      }
-    }
-
-    proxy me = new proxy(localPort, 
-                         remoteHost, (remotePort == 0 ? 23 : remotePort));
-  }
-
-  /**
-   * Cycle around until an error occurs and wait for incoming connections.
-   * An incoming connection will create two redirectors. One for
-   * local-host - destination-host and one for destination-host - local-host.
-   */
-  public void run()
-  {
-    while(true)
-    {
-      Socket localSocket = null;
-      try {
-        localSocket = server.accept();
-      } catch(Exception e) {
-        System.err.println("proxy: error: accept connection failed");
-        continue;
-      }
-      log("accepted connection from "+
-          localSocket.getInetAddress().getHostName());
-      try {
-        Socket destinationSocket = new Socket(remoteHost, remotePort);
-        log("connecting "+localSocket.getInetAddress().getHostName()+" <-> "+
-            destinationSocket.getInetAddress().getHostName());
-        redirector r1 = new redirector(localSocket, destinationSocket);
-        redirector r2 = new redirector(destinationSocket, localSocket);
-        r1.couple(r2);
-        r2.couple(r1);
-      } catch(Exception e) {
-        System.err.println("proxy: error: cannot create sockets");
-        try {
-          DataOutputStream os = new DataOutputStream(localSocket.getOutputStream());
-          os.writeChars("Remote host refused connection.\n");
-          localSocket.close();
-        } catch(IOException ioe) { ioe.printStackTrace(); }
-        continue;
-      }
-    }
-  }
-
-  private void log(String msg)
-  {
-    System.out.println("proxy: ["+new Date()+"] "+msg);
-  }
-}
-
-/**
- * A class useful for the proxy server.
- * This class takes over control of newly created connections and redirects
- * the data streams.
- */
-class redirector implements Runnable
-{
-  private redirector companion = null;
-  private Socket localSocket, remoteSocket;
-  private InputStream from;
-  private OutputStream to;
-  private byte[] buffer = new byte[4096];
-
-  /**
-   * redirector gets the streams from sockets
-   */
-  public redirector(Socket local, Socket remote)
-  {
-    try {
-      localSocket = local; 
-      remoteSocket = remote;
-      from = localSocket.getInputStream();
-      to = remoteSocket.getOutputStream();
-    } catch(Exception e) {
-      System.err.println("redirector: cannot get streams");
-    }
-  }
-
-  /**
-   * couple this redirector instance with another one (usually the other
-   * direction of the connection)
-   */
-  public void couple(redirector c) { 
-    companion = c; 
-    Thread listen = new Thread(this);
-    listen.start();
-  }
-
-  /**
-   * decouple us from our companion. This will let this redirector die after
-   * exiting from run()
-   */
-  public void decouple() { companion = null; }
-
-  /**
-   * read data from the input and write it to the destination stream until
-   * an error occurs or our companion is decoupled from us
-   */
-  public void run()
-  {
-    int count;
-    try {
-      while(companion != null) {
-        if((count = from.read(buffer)) < 0)
-          break;
-        to.write(buffer, 0, count);
-      }
-    } catch(Exception e) {
-      System.err.println("redirector: connection lost");
-    }
-    try {
-      from.close();
-      to.close();
-      localSocket.close();
-      remoteSocket.close();
-      // is our companion dead? no, then decouple, because we die
-      if(companion != null) companion.decouple();
-    } catch(Exception io) {
-      System.err.println("redirector: error closing streams and sockets");
-      io.printStackTrace();
-    }
-  }
-}
-
-      
diff --git a/web/root/telnet/tools/redirector.class b/web/root/telnet/tools/redirector.class
deleted file mode 100644
index 4498f80091a1c4f9f291c23f9566a00b08ebb808..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1966
zcmX^0Z`VEsW(Hk`G7g3u4u)I~hCFtLNOp!Oc7|wnh8T8+Sayauc7}L%h6HwoM0SQG
zc7_aghHNed4h9u225tsbP6mDkH7*7g26Zk5RuI9<pbiq#;9}rs(Bx#`V$cF{wYeC0
z8FWB|E{Lng#lXX$&&9yTU;xr<2of^_nP&`QnShKk<zcXAumBO3JPf`JK0FNmAZ`GI
z7Y{=)h!w)%%EJ)K;LO7i#$d+75YAxE!;s40!NZUSl1*o@;$g^Qu;pPW0ErcW#7Y=k
zco<3<>=+ruH8g#)63Y_xGxPO5^9o8!f=h~06LYN@86=Qo{7XxarI<A|!$4XgI&%{9
z()EK%^YRjta#F29GNKwtGQlN9nR)48aaE{zUTTScaDH-jYKb*OA(8+{Kfi`gQEEzN
zQEGBYei2wFt46ewrY9o<yGFE=rzc1an@wh3W{Djm1B-KhN-84*qbDN+zfWdfs$Xet
zQfg5M$T5rzq7d^jJSPShNAns&LqJhx9wIcL;$XkJRwSnulw{`TSu-*SK@0+mhh!9`
zCZ<?3G6+Hhk$eSGn4g@O6P8$%333_Kc|6GeWMtq9&Mz%WPIb%7No8bUigsdTU`r}Z
zOG_<cWME8YWMECs$uCZ2WZ+EB&n-yI%goOMiRG8(l`t}}CFhqGfV6R>q{5iYsYOMM
z3@m9y`MHb?eCeqrND<7)z@MI4g6w%l2Byq>Mh0OlsezF}1SX2A1Fje;uwX`jg9IK_
zj0^%`Bfz5mNm;4MC5#M02q9SdK~Wc6SzMBuiy{OGV-!0=GK%ucL4nH1zz;SM5$=o(
zY&n_5C8?l*$^nHxBLg>>hNM152L1v_EGH&shZH3yr!q3IgGF*cItq&ND=HZoxWKd?
z$Y@3e7Ely3GH@Z%CnJL#f@7tSoS2uFU!stnTB1-4&Pv6M4ALkH^7HailR?QxAt%4M
zgpolPSvIw(D8EPn6w#S^=`a%%67y0NioqGCn2~|4C^a{~B$bhY2f~2)g}JB{<m%#*
z#G(>L29}bH%wk3crjmR{2G;VT%#u_F1qOZw4hBXBP6jpxCWZh8F9rq%CQwNMrVAK6
zz;q~sGng)6Z~@c43_c7D3@i+c4E7Ax42%p83=9k#8JHND85kJswYD<|XfbYO5YrMA
zVA#X4l|e{Ldn<#Cmd;iN1ud;D3^Lmoq_#3hf|v^17^Jr{C@p8;3E#vZA;h?iL2e@h
zBZClw5rZiMI|CDg69WT-5d#kc1H*C#7KV8YoD7Q?gcw#b2r;Z;5NBA;Aj7bRL7ibO
zgAv0@22+N03}y`L8SEIAGk7vEFepHr!%z(N6a#}jLkfcj10%?Z4EYQm3``7+3_%Pw
zQ0K=oFoV?#gB&Zin}Iu0YdeFWug*3G#f@M;gPbD7z{0@5u!Dh>VJ8D0!!8CPhV4jJ
z3B#=tX7FS1Vqj$8V_;=SW^e~v70h4(waNg@XJB9hhZ4ecHn8Ikf=%QBv)P~~GBOmx
zbTjfXuz($!x12#iYYT&<))oe#Z4B~}HnSPTwYD%Qf%weQ+ZdF!wlIht|9>AAxJ=s^
z<Us1TF$jZ{ZDSCD#D%EV76yfF3?NGE7=si@*ER<6<qQJ8peSM9#vrG&jX@R^y9^BK
z3|b8A48{zx3<>b)@naBXU|=}Kz`$^tftle9gD}H61_g%G4C)N07_=BJFlaMeWH4s9
z%wWuLg~5v9DuW}#H3koc>kR%3HyL6XE;A%B++s*%xXqBsaEBq6;W*g$THtWbgZf^8
fL4YBU!I6QHL7jma%1UJ5VTfRG1BawHIDRq#lwZOZ

diff --git a/web/root/telnet/tools/relayd b/web/root/telnet/tools/relayd
deleted file mode 100644
index 8844ac1707c457d32930d644f461d030b5af9580..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 16896
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
YK!){@)qwU>BlF`?cX~ticOxqR0IbPHng9R*

diff --git a/web/root/telnet/tools/relayd.c b/web/root/telnet/tools/relayd.c
deleted file mode 100644
index 66fdc22e95..0000000000
--- a/web/root/telnet/tools/relayd.c
+++ /dev/null
@@ -1,448 +0,0 @@
-/**
- * relayd.c -- a relay daemon (using one targethost/port)
- * --
- * $Id: relayd.c,v 1.1.1.1 2005/09/25 22:40:20 rswindell Exp $
- *
- * This 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, or (at your option)
- * any later version.
- *
- * "The Java Telnet Applet" 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 this software; see the file COPYING.  If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/* relayd.c (c) 1996,1997 Marcus Meissner <marcus@mud.de> */
-
-/* Compile with: cc -o relayd relayd.c
- *           or: gcc -o relayd relayd.c
- * Solaris: (g)cc -o relayd relayd.c -lsocket -lnsl
- */
-
-/* this program expects the string:
- * "relay <hostname> <port>" or "relay <hostname>"
- * after connecting. It will then try to connect to the specified host
- * if failures occur, it will terminate the connection.
- */
-
-/* adjust this to a reasonable limit */
-#define MAXUSERS  120
-
-/* message printed if all slots are used ... */
-#define FAILMESSAGE "Sorry, all slots are full.\r\n"
-
-/* string printed before connection */
-#define RELAYHEADER "Relayd (c) Marcus Meissner\r\n"
-
-#include <stdio.h>
-#ifdef _WIN32
-#include <stdlib.h>
-#include <errno.h>
-#include <signal.h>
-#include <winsock.h>
-#include <io.h>
-#define ioctl ioctlsocket
-#define write(h,buf,size) send(h,buf,size,0)
-#define read(a,b,c) recv(a,b,c,0)
-#define close _lclose
-#define EINPROGRESS WSAEWOULDBLOCK
-#else
-#include <unistd.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/errno.h>
-#include <sys/signal.h>
-#include <sys/fcntl.h>
-#include <sys/ioctl.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#endif
-#include <memory.h>
-#include <malloc.h>
-#include <string.h>
-#include <sys/types.h>
-
-
-
-#if defined(sun) && defined(__GNUC__)
-int socket(int,int,int);
-int shutdown(int,int);
-int close(int);
-int bind(int,struct sockaddr*,int);
-int listen(int,int);
-void bzero(char*,int);
-int select(int,fd_set *,fd_set*,fd_set*,struct timeval*);
-int accept(int,struct sockaddr*,int*);
-int connect(int,struct sockaddr*,int);
-int recvfrom(int,char*,int,int,struct sockaddr*,int*);
-/*void perror(char*); SLOWLARIS HASS*/
-/*int sendto(int,char*,int,int,struct sockaddr*,int); SLOWLARIS HASS*/
-#endif
-
-#ifdef hpux
-/* redefinition... to avoid prototype in <time.h> */
-#define FD_CAST int
-#endif
-
-#ifdef sgi
-void bzero(void*,int);
-#endif
-
-#ifndef FD_CAST
-#define FD_CAST fd_set
-#endif
-
-extern int errno;
-
-char  *inbuf[MAXUSERS],*outbuf[MAXUSERS];
-int insize[MAXUSERS],outsize[MAXUSERS];
-int incur[MAXUSERS],outcur[MAXUSERS];
-int outfd[MAXUSERS],infd[MAXUSERS];
-struct  sockaddr_in inaddr[MAXUSERS],outaddr[MAXUSERS];
-
-#ifdef _WIN32
-#define perror xperror
-void xperror(char *str) {
-	fprintf(stderr,"%s: %d\n",str,GetLastError());
-}
-#endif
-
-static int
-fd_make_nonblocking(int fd) {
-  int     isnonblock=0;
-
-#ifdef FIONBIO
-  if (!isnonblock) {
-    int     b;
-    b=1;
-    if (-1==ioctl(fd,FIONBIO,&b)) {
-      perror("ioctl FIONBIO");
-    } else
-      isnonblock=1;
-  }
-#endif
-#ifdef O_NDELAY
-  if (!isnonblock) {
-    int     flags;
-
-    if (-1==(flags=fcntl(fd,F_GETFL))) {
-      perror("fcntl F_GETFL");
-    } else {
-      flags|=O_NDELAY;
-      if (-1==fcntl(fd,F_SETFL,flags)) {
-        perror("fcntl F_SETFL  O_NDELAY");
-      } else
-        isnonblock=1;
-    }
-  }
-#endif
-#ifdef O_NONBLOCK
-  if (!isnonblock) {
-    int     flags;
-
-    if (-1==(flags=fcntl(fd,F_GETFL))) {
-      perror("fcntl F_GETFL");
-    } else {
-      flags|=O_NONBLOCK;
-      if (-1==fcntl(fd,F_SETFL,flags)) {
-        perror("fcntl F_SETFL  O_NONBLOCK");
-      } else
-        isnonblock=1;
-    }
-  }
-#endif
-  return isnonblock;
-}
-
-void
-clean_connection(i)
-int i;
-{
-  if (outfd[i]>=0) {
-    if (-1==close(outfd[i]))
-      perror("close");
-    outfd[i]=-1;
-  }
-  if (infd[i]>=0) {
-    if (-1==close(infd[i]))
-      perror("close");
-    infd[i]=-1;
-  }
-  incur[i]=outcur[i]=0;
-  outbuf[i][0]=inbuf[i][0]='\0';
-}
-
-void
-usage() {
-   fprintf(stderr,"Usage: relayd <listenport> <targethost> [<targetport>]\n");
-}
-
-void
-main(argc,argv)
-int argc;
-char  **argv;
-{
-  int i,j,res;
-  int acfd;
-  struct  sockaddr_in acsa;
-  char  readbuf[1000],relaystring[1000];
-  struct  in_addr targetaddr;
-  struct  hostent *hp;
-  char *targethost;
-  int  port,targetport;
-  
-#ifdef _WIN32 
-  {
-	WSADATA wsad;
-	
-	WSAStartup(0x0101,&wsad);
-  }
-#else
-  close(0);
-  close(1);
-#endif
-
-#ifdef SIGPIPE
-  signal(SIGPIPE,SIG_IGN);
-#endif  
-  switch (argc) {
-  default:
-  case 1:
-  case 2:
-    usage();
-    exit(1);
-  case 3:
-    if (!sscanf(argv[1],"%d",&port)) {
-      usage();
-      exit(1);
-    }
-    targethost = argv[2];
-    targetport = 23;
-    break;
-  case 4:
-    if (!sscanf(argv[1],"%d",&port)) {
-      usage();exit(1);
-    }
-    if (!sscanf(argv[3],"%d",&targetport)) {
-      usage();exit(1);
-    }
-    targethost=argv[2];
-    break;
-  }
-  strcpy(relaystring,FAILMESSAGE);
-  if (-1==(acfd=socket(PF_INET,SOCK_STREAM,0))) {
-    perror("socket(accept_socket)");
-    exit(1);
-  }
-  for (i=MAXUSERS;i--;) {
-    inbuf[i]=malloc(10000*sizeof(char));
-    outbuf[i]=malloc(10000*sizeof(char));
-    inbuf[i][0]='\0';outbuf[i][0]='\0';
-
-    insize[i]=10000;incur[i]=0;
-
-    outsize[i]=10000;outcur[i]=0;
-    outfd[i]=infd[i]=-1;
-  }
-
-  acsa.sin_family=AF_INET;
-  acsa.sin_port=htons(port);
-  acsa.sin_addr.s_addr=INADDR_ANY;
-  if (-1==bind(acfd,(struct sockaddr*)&acsa,sizeof(struct sockaddr_in))) {
-    perror("bind");
-    exit(1);
-  }
-  /* 5 is usual the maximum anyway */
-  if (-1==listen(acfd,5)) {
-    perror("listen");
-    exit(1);
-  }
-  while (1) {
-    fd_set  readfds,writefds;
-    int width;
-
-    width=3;
-    if (acfd>=width)
-      width=acfd+1;
-restart_select:
-    FD_ZERO(&readfds);FD_ZERO(&writefds);
-    FD_SET(acfd,&readfds);
-    for (i=MAXUSERS;i--;) {
-      if (outfd[i]>=0) {
-        /*need to do that... else it will cause load 1*/
-        if (incur[i])
-          FD_SET(outfd[i],&writefds);
-        FD_SET(outfd[i],&readfds);
-        if (outfd[i]>=width)
-          width=outfd[i]+1;
-      }
-      if (infd[i]>=0) {
-        /*need to do that... else it will cause load 1*/
-        if (outcur[i])
-          FD_SET(infd[i],&writefds);
-        FD_SET(infd[i],&readfds);
-        if (infd[i]>=width)
-          width=infd[i]+1;
-      }
-    }
-    if (-1==select(   width,
-          (FD_CAST*)&readfds,
-          (FD_CAST*)&writefds,
-          NULL,/*no exceptfds.*/
-          0)
-    ) {
-      if (errno!=EINTR)
-        perror("select");
-      else
-        goto  restart_select;
-    }
-    if (FD_ISSET(acfd,&readfds)) {
-      int afd;
-      int aclen;
-      struct  sockaddr_in conaddr;
-
-      aclen=sizeof(struct sockaddr_in);
-      if (-1==(afd=accept(acfd,(struct sockaddr*)&conaddr,&aclen)))
-        perror("accept");
-      for (i=MAXUSERS;i--;)
-        if ((infd[i]==-1) && (outfd[i]==-1))
-          break;
-      if (i==-1) {
-        write(afd,relaystring,strlen(relaystring));
-        close(afd);
-      } else {
-        char  sendbuf[200];
-        infd[i]=afd;
-        memcpy(&inaddr[i],&conaddr,sizeof(struct sockaddr_in));
-        /* outfd setting delayed until we get
-         * to the first line
-         */
-        hp=gethostbyname(targethost);
-        if (!hp) {/* not found */
-          clean_connection(i);
-          continue;
-        }
-        memcpy(&targetaddr,hp->h_addr_list[0],sizeof(struct in_addr));
-        outaddr[i].sin_family=AF_INET;
-        outaddr[i].sin_port=htons(targetport);
-        memcpy(&(outaddr[i].sin_addr),&targetaddr,4);
-        strcpy(sendbuf,RELAYHEADER);
-        outcur[i]=strlen(sendbuf);
-        memcpy(outbuf[i],sendbuf,strlen(sendbuf)+1);
-        if (-1==(outfd[i]=socket(PF_INET,SOCK_STREAM,0)))
-          perror("socket(connect_socket)");
-#ifndef _WIN32
-        (void)fd_make_nonblocking(outfd[i]);
-#endif        
-		if (  (-1==connect( outfd[i],
-            (struct sockaddr*)&outaddr[i],
-            sizeof(struct sockaddr_in))
-#ifdef _WIN32
-        ) && (WSAGetLastError()!=WSAEWOULDBLOCK)
-#else  
-        ) && (errno!=EINPROGRESS)
-#endif
-        ) {
-#ifdef _WIN32
-			sprintf(readbuf,"Connect to %s failed: %d\n",targethost,GetLastError());
-#else
-          sprintf(readbuf,"Connect to %s failed.\n",targethost);
-#endif
-          perror("connect");
-          if (-1==write(infd[i],readbuf,strlen(readbuf)))
-            perror("write");
-          clean_connection(i);
-          continue;
-        }
-        inbuf[i][0]='\0';
-        incur[i]=0;
-      }
-    }
-    for (i=MAXUSERS;i--;) {
-      if ((infd[i]>=0) && FD_ISSET(infd[i],&readfds)) {
-        do {
-          if (-1==(res=read(infd[i],readbuf,1000))) {
-            if (errno==EINTR)
-              continue;
-            /* user side has broken the connection */
-            clean_connection(i);
-            break;
-          }
-          break;
-        } while (1);
-        if (res==0) /* EOF */
-          clean_connection(i);
-        if (res>0) {
-          readbuf[res]='\0';
-          while (incur[i]+res>=insize[i]) {
-            inbuf[i]=realloc(inbuf[i],insize[i]*2);
-            insize[i]*=2;
-          }
-          memcpy(inbuf[i]+incur[i],readbuf,res+1);
-          incur[i]+=res;
-        }
-      }
-      if ((outfd[i]>=0) && FD_ISSET(outfd[i],&readfds)) {
-        do {
-          if (-1==(res=read(outfd[i],readbuf,1000))) {
-            if (errno==EINTR)
-              continue;
-            /* the mudside has broken the
-             * connection. we still have
-             * to transmit the rest of
-             * the text
-             */
-            outfd[i]=-1;
-            break;
-          }
-          break;
-        } while (1);
-        if (res==0)
-          clean_connection(i);
-        if (res>0) {
-          /* 0 is not automagically appended. */
-          readbuf[res]='\0';
-          while (outcur[i]+res>=outsize[i]) {
-            outbuf[i]=realloc(outbuf[i],outsize[i]*2);
-            outsize[i]*=2;
-          }
-          memcpy(outbuf[i]+outcur[i],readbuf,res+1);
-          outcur[i]+=res;
-        }
-      }
-      if ((infd[i]>=0) && FD_ISSET(infd[i],&writefds)) {
-        j=outcur[i];
-        if (-1==(res=write(infd[i],outbuf[i],j))) {
-          if (errno!=EINTR) {
-            clean_connection(i);
-          }
-        }
-        if (res>0) {
-          memcpy(outbuf[i],outbuf[i]+res,outcur[i]-res);
-          outcur[i]-=res;
-        }
-      }
-      if ((outfd[i]>=0) && FD_ISSET(outfd[i],&writefds)) {
-        j=incur[i];
-        if (-1==(res=write(outfd[i],inbuf[i],j))) {
-          if (errno!=EINTR) {
-            outfd[i]=-1;
-            /*clean_connection(i);*/
-          }
-        }
-        if (res>0) {
-          memcpy(inbuf[i],inbuf[i]+res,incur[i]-res);
-          incur[i]-=res;
-        }
-      }
-    }
-  }
-}
diff --git a/web/root/telnet/tools/relayd.exe b/web/root/telnet/tools/relayd.exe
deleted file mode 100755
index dec7203e0165ab34e6f255165cd12ffb0a183322..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 38400
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

-- 
GitLab