diff --git a/exec/fido-nodelist-browser.js b/exec/fido-nodelist-browser.js
index 0e2dce1cc3c6d1f49049a8c17df8ab40d046c8f4..743c063a8851cac906ee1afcc71e213134f0f124 100644
--- a/exec/fido-nodelist-browser.js
+++ b/exec/fido-nodelist-browser.js
@@ -6,341 +6,326 @@ require('fido_syscfg.js', 'FTNDomains');
 require('ftn_nodelist.js', 'NodeList');
 
 function node_info_popup(node, frame, settings) {
-  const fmt = "%10s: \1+\1h\1c%s\1-\r\n";
-  const popup = new Frame(
-    frame.x + 1,
-    Math.floor((frame.y + frame.height - 17) / 2),
-    frame.width - 2,
-    17,
-    BG_BLUE|WHITE,
-    frame
-  );
-  popup.putmsg('\r\n Node Details');
-  popup.gotoxy(1, 4);
-  popup.putmsg(format(fmt, 'Address', node.addr));
-  popup.putmsg(format(fmt, 'Name', node.name));
-  popup.putmsg(format(fmt, 'Sysop', node.sysop));
-  popup.putmsg(format(fmt, 'Location', node.location));
-  popup.putmsg(format(fmt, 'Hub', node.hub));
-  if (node.flags.INA || node.flags.IP) {
-    popup.putmsg(format(fmt, 'Internet', node.flags.INA || node.flags.IP));
-  }
-  const protocol = [];
-  if (node.flags.IBN) protocol.push('Binkp');
-  if (node.flags.IFC) protocol.push('ifcico');
-  if (node.flags.ITN) protocol.push('Telnet');
-  if (node.flags.IVM) protocol.push('Vmodem');
-  if (protocol.length) {
-    popup.putmsg(format(fmt, 'Protocol', protocol.join(', ')));
-  }
-  protocol = undefined;
-  const email = [];
-  if (node.flags.IEM) email.push(node.flags.IEM);
-  if (node.flags.ITX) email.push('TransX');
-  if (node.flags.IUC) email.push('UUEncode');
-  if (node.flags.IMI) email.push('MIME');
-  if (node.flags.ISE) email.push('SEAT');
-  if (email.length) popup.putmsg(format(fmt, 'Email', email.join(', ')));
-  email = undefined;
-  if (node.flags.PING) popup.putmsg('Accepts PING\r\n');
-  var other = [
-    'ZEC', 'REC', 'NEC', 'NC', 'SDS', 'SMH', 'RPK', 'NPK', 'ENC', 'CDP'
-  ].filter(function (e) {
-    return typeof node.flags[e] != 'undefined';
-  });
-  if (other.length) popup.putmsg(format(fmt, 'Flags', other.join(', ')));
-  other = ['Private', 'Hold', 'Down'].reduce(function (a, c) {
-    if (node[c.toLowerCase()]) a.push(c);
-    return a;
-  }, []);
-  if (other.length) popup.putmsg(format(fmt, 'Status', other.join(', ')));
-  other = undefined;
-  popup.gotoxy(1, 16);
-  popup.putmsg(' \1h\1cS\1h\1wend netmail  \1h\1cC\1h\1wlose');
-  popup.open();
-  frame.cycle();
-  console.gotoxy(console.screen_columns, console.screen_rows);
-  var user_input;
-  while((user_input = console.inkey(K_NONE).toLowerCase()) == '') {
-    yield();
-  }
-  popup.close();
-  if (user_input == 's') {
-    console.clear(LIGHTGRAY);
-    bbs.netmail(node.sysop + '@' + node.addr, WM_NETMAIL);
-    console.clear(LIGHTGRAY);
-    settings.frame.invalidate();
-  }
+	const fmt = "%10s: \1+\1h\1c%s\1-\r\n";
+	const popup = new Frame(
+		frame.x + 1,
+		Math.floor((frame.y + frame.height - 17) / 2),
+		frame.width - 2,
+		17,
+		BG_BLUE|WHITE,
+		frame
+	);
+	popup.putmsg('\r\n Node Details');
+	popup.gotoxy(1, 4);
+	popup.putmsg(format(fmt, 'Address', node.addr));
+	popup.putmsg(format(fmt, 'Name', node.name));
+	popup.putmsg(format(fmt, 'Sysop', node.sysop));
+	popup.putmsg(format(fmt, 'Location', node.location));
+	popup.putmsg(format(fmt, 'Hub', node.hub));
+	if (node.flags.INA || node.flags.IP) {
+		popup.putmsg(format(fmt, 'Internet', node.flags.INA || node.flags.IP));
+	}
+	const protocol = [];
+	if (node.flags.IBN) protocol.push('Binkp');
+	if (node.flags.IFC) protocol.push('ifcico');
+	if (node.flags.ITN) protocol.push('Telnet');
+	if (node.flags.IVM) protocol.push('Vmodem');
+	if (protocol.length) {
+		popup.putmsg(format(fmt, 'Protocol', protocol.join(', ')));
+	}
+	protocol = undefined;
+	const email = [];
+	if (node.flags.IEM) email.push(node.flags.IEM);
+	if (node.flags.ITX) email.push('TransX');
+	if (node.flags.IUC) email.push('UUEncode');
+	if (node.flags.IMI) email.push('MIME');
+	if (node.flags.ISE) email.push('SEAT');
+	if (email.length) popup.putmsg(format(fmt, 'Email', email.join(', ')));
+	email = undefined;
+	if (node.flags.PING) popup.putmsg('Accepts PING\r\n');
+	var other = [
+		'ZEC', 'REC', 'NEC', 'NC', 'SDS', 'SMH', 'RPK', 'NPK', 'ENC', 'CDP'
+	].filter(function (e) {
+		return typeof node.flags[e] != 'undefined';
+	});
+	if (other.length) popup.putmsg(format(fmt, 'Flags', other.join(', ')));
+	other = ['Private', 'Hold', 'Down'].reduce(function (a, c) {
+		if (node[c.toLowerCase()]) a.push(c);
+		return a;
+	}, []);
+	if (other.length) popup.putmsg(format(fmt, 'Status', other.join(', ')));
+	other = undefined;
+	popup.gotoxy(1, 16);
+	popup.putmsg(' \1h\1cS\1h\1wend netmail  \1h\1cC\1h\1wlose');
+	popup.open();
+	frame.cycle();
+	console.gotoxy(console.screen_columns, console.screen_rows);
+	var user_input;
+	while((user_input = console.inkey(K_NONE).toLowerCase()) == '') {
+		yield();
+	}
+	popup.close();
+	if (user_input == 's') {
+		console.clear(LIGHTGRAY);
+		bbs.netmail(node.sysop + '@' + node.addr, WM_NETMAIL);
+		console.clear(LIGHTGRAY);
+		settings.frame.invalidate();
+	}
 }
 
 function populate_node_tree(filename, zone, net, tree, settings) {
-  const nodelist = new NodeList(filename);
-  nodelist.entries.forEach(function (e) {
-    const z = e.addr.split(':')[0];
-    if (z != zone) return;
-    const n = e.addr.match(/\d+:(\d+)\//)[1];
-    if (n != net) return;
-    const w = Math.floor(((tree.frame.width - 15 - 3) / 2) - 1);
-    tree.addItem(format(
-      '%-15s%-' + w + 's%-' + w + 's',
-      e.addr, e.name.substr(0, w), e.location.substr(0, w)
-    ), node_info_popup, e, tree.frame, settings);
-  });
-  // Not sure if it's worth sorting this tree
-  // Nodes probably could be out of order but they generally appear to be in
-  // sequence by the time we get here.
+	const nodelist = new NodeList(filename);
+	nodelist.entries.forEach(function (e) {
+		const z = e.addr.split(':')[0];
+		if (z != zone) return;
+		const n = e.addr.match(/\d+:(\d+)\//)[1];
+		if (n != net) return;
+		const w = Math.floor(((tree.frame.width - 15 - 3) / 2) - 1);
+		tree.addItem(format(
+			'%-15s%-' + w + 's%-' + w + 's',
+			e.addr, e.name.substr(0, w), e.location.substr(0, w)
+		), node_info_popup, e, tree.frame, settings);
+	});
+	// Not sure if it's worth sorting this tree
+	// Nodes probably could be out of order but they generally appear to be in
+	// sequence by the time we get here.
 }
 
 function populate_net_tree(filename, zone, tree, settings) {
-  const nodelist = new NodeList(filename);
-  const nets = [];
-  nodelist.entries.forEach(function (e) {
-    const zn = e.addr.match(/(\d+):(\d+)\//);
-    if (zn[1] != zone) return;
-    if (nets.indexOf(zn[2]) > -1) return;
-    nets.push(zn[2]);
-    const net_tree = tree.addTree('Net ' + zn[2]);
-    const idx = tree.items.length - 1;
-    net_tree.onOpen = function () {
-      populate_node_tree(filename, zone, zn[2], net_tree, settings);
-      if (settings.auto_close_net) {
-        tree.items.forEach(function (e) {
-          if (e.text != net_tree.text) {
-            e.close();
-            e.index = -1;
-          }
-        });
-      }
-      tree.refresh();
-    }
-    net_tree.onClose = function () {
-      net_tree.items = [];
-    }
-  });
-  tree.items.sort(function (a, b) {
-    if (parseInt(a.text.split(' ')[1]) < parseInt(b.text.split(' ')[1])) {
-      return -1;
-    } else {
-      return 1;
-    }
-  });
+	const nodelist = new NodeList(filename);
+	const nets = [];
+	nodelist.entries.forEach(function (e) {
+		const zn = e.addr.match(/(\d+):(\d+)\//);
+		if (zn[1] != zone) return;
+		if (nets.indexOf(zn[2]) > -1) return;
+		nets.push(zn[2]);
+		const net_tree = tree.addTree('Net ' + zn[2]);
+		const idx = tree.items.length - 1;
+		net_tree.onOpen = function () {
+			populate_node_tree(filename, zone, zn[2], net_tree, settings);
+			if (settings.auto_close_net) {
+				tree.items.forEach(function (e) {
+			    	if (e.text != net_tree.text) {
+			    		e.close();
+			    		e.index = -1;
+			    	}
+			  	});
+			}
+			tree.refresh();
+		}
+		net_tree.onClose = function () {
+			net_tree.items = [];
+		}
+	});
+	tree.items.sort(function (a, b) {
+		if (parseInt(a.text.split(' ')[1]) < parseInt(b.text.split(' ')[1])) {
+			return -1;
+		} else {
+			return 1;
+		}
+	});
 }
 
 function populate_zone_tree(filename, tree, settings) {
-  const nodelist = new NodeList(filename);
-  const zones = [];
-  nodelist.entries.forEach(function (e) {
-    const zone = e.addr.split(':')[0];
-    if (zones.indexOf(zone) > -1) return;
-    zones.push(zone);
-    const zone_tree = tree.addTree('Zone ' + zone);
-    const idx = tree.items.length - 1;
-    zone_tree.onOpen = function () {
-      populate_net_tree(filename, zone, zone_tree, settings);
-      if (settings.auto_close_zone) {
-        tree.items.forEach(function (e) {
-          if (e.text != zone_tree.text) {
-            e.close();
-            e.index = -1;
-          }
-        });
-      }
-      tree.refresh();
-    }
-    zone_tree.onClose = function () {
-      zone_tree.items = [];
-    }
-  });
-  tree.items.sort(function (a, b) {
-    if (parseInt(a.text.split(' ')[1]) < parseInt(b.text.split(' ')[1])) {
-      return -1;
-    } else {
-      return 1;
-    }
-  });
+	const nodelist = new NodeList(filename);
+	const zones = [];
+	nodelist.entries.forEach(function (e) {
+		const zone = e.addr.split(':')[0];
+		if (zones.indexOf(zone) > -1) return;
+		zones.push(zone);
+		const zone_tree = tree.addTree('Zone ' + zone);
+		const idx = tree.items.length - 1;
+		zone_tree.onOpen = function () {
+			populate_net_tree(filename, zone, zone_tree, settings);
+			if (settings.auto_close_zone) {
+				tree.items.forEach(function (e) {
+			    	if (e.text != zone_tree.text) {
+			    		e.close();
+			    		e.index = -1;
+			    	}
+			  	});
+			}
+			tree.refresh();
+		}
+		zone_tree.onClose = function () {
+			zone_tree.items = [];
+		}
+	});
+	tree.items.sort(function (a, b) {
+		if (parseInt(a.text.split(' ')[1]) < parseInt(b.text.split(' ')[1])) {
+			return -1;
+		} else {
+			return 1;
+		}
+	});
 }
 
-function populate_domain_tree(filename, domain, tree, settings) {
-  try {
-    const nodelist = new NodeList(filename);
-  } catch (err) {
-    log(LOG_ERR, format('Error parsing %s: %s', filename, err));
-    return;
-  }
-  const domain_tree = tree.addTree(domain);
-  const idx = tree.items.length - 1;
-  domain_tree.onOpen = function () {
-    populate_zone_tree(filename, domain_tree, settings);
-    if (settings.auto_close_domain) {
-      tree.items.forEach(function (e) {
-        if (e.text != domain_tree.text) {
-          e.close();
-          e.index = -1;
-        }
-      });
-    }
-    tree.refresh();
-  }
-  domain_tree.onClose = function () {
-    domain_tree.items = [];
-  }
+function populate_domain_tree(filename, domain, tree, settings, frame) {
+	try {
+		const nodelist = new NodeList(filename);
+	} catch (err) {
+		log(LOG_ERR, format('Error parsing %s: %s', filename, err));
+		return;
+	}
+	const domain_tree = tree.addTree(domain);
+	const idx = tree.items.length - 1;
+	var sframe;
+	domain_tree.onOpen = function () {
+		populate_zone_tree(filename, domain_tree, settings);
+		if (settings.auto_close_domain) {
+			tree.items.forEach(function (e) {
+				if (e.text != domain_tree.text) {
+			    	e.close();
+			    	e.index = -1;
+				}
+			});
+		}
+		if (nodelist.domain && nodelist.date) {
+			sframe = new Frame(frame.x, frame.y + 1, frame.width, 1, frame.attr, frame);
+			sframe.open();
+			sframe.putmsg(nodelist.domain + ' nodelist for ' + nodelist.date);
+		}
+		tree.refresh();
+	}
+	domain_tree.onClose = function () {
+		domain_tree.items = [];
+		if (sframe instanceof Frame) sframe.delete();
+	}
 }
 
-function populate_tree(tree, settings) {
-  const ftn_domains = new FTNDomains();
-  Object.keys(ftn_domains.nodeListFN).forEach(function (e) {
-    if (!file_exists(ftn_domains.nodeListFN[e])) return;
-    const dn = settings['domain_' + e] || e;
-    populate_domain_tree(ftn_domains.nodeListFN[e], dn, tree, settings);
-  });
-  Object.keys(settings).forEach(function (e) {
-    if (e.search(/^nodelist_/) > -1 && file_exists(settings[e])) {
-      const dn = e.replace(/^nodelist_/, '');
-      populate_domain_tree(settings[e], dn, tree, settings);
-    }
-  });
-  tree.items.sort(function (a, b) {
-    return a.text.toLowerCase() < b.text.toLowerCase() ? -1 : 1;
-  });
-  tree.refresh();
+function populate_tree(tree, settings, frame) {
+	const ftn_domains = new FTNDomains();
+	Object.keys(ftn_domains.nodeListFN).forEach(function (e) {
+		if (!file_exists(ftn_domains.nodeListFN[e])) return;
+		const dn = settings['domain_' + e] || e;
+		populate_domain_tree(ftn_domains.nodeListFN[e], dn, tree, settings, frame);
+	});
+	Object.keys(settings).forEach(function (e) {
+		if (e.search(/^nodelist_/) > -1 && file_exists(settings[e])) {
+			const dn = e.replace(/^nodelist_/, '');
+			populate_domain_tree(settings[e], dn, tree, settings, frame);
+		}
+	});
+	tree.items.sort(function (a, b) {
+		return a.text.toLowerCase() < b.text.toLowerCase() ? -1 : 1;
+	});
+	tree.refresh();
 }
 
 function search(frame, settings) {
-
-  const sframe = new Frame(frame.x, frame.y + 4, frame.width, 3, BG_BLUE|WHITE, frame);
-  sframe.putmsg('Search by address, system name, sysop, or location:');
-  sframe.gotoxy(1, 3);
-  sframe.putmsg("Type a query and wait for results, don't hit enter.");
-  sframe.open();
-
-  const typeahead = new Typeahead({
-    x : 1,
-    y : sframe.y + 1,
-    bg : 0,
-    prompt: '',
-    frame: sframe,
-    datasources: [
-      function (str) {
-        const w = (Math.floor((frame.width - 9 - 15) / 3) - 1);
-        const ret = [];
-        const ftn_domains = new FTNDomains();
-        const files = Object.keys(ftn_domains.nodeListFN).map(function (e) {
-          return ftn_domains.nodeListFN[e];
-        });
-        Object.keys(settings).forEach(function (e) {
-          if (e.search(/^nodelist_/) > -1) files.push(settings[e]);
-        });
-        files.forEach(function (e) {
-          if (!file_exists(e)) return
-          const nodelist = new NodeList(e);
-          nodelist.entries.forEach(function (e) {
-            var r = ['addr', 'sysop', 'name', 'location'].some(function (ee) {
-              if (e[ee].search(new RegExp(str, 'i')) > -1) {
-                ret.push({
-                  node : e,
-                  text : format(
-                    '%-15s%-' + w + 's %-' + w + 's %-' + w + 's',
-                    e.addr,
-                    e.name.substr(0, w),
-                    e.location.substr(0, w),
-                    e.sysop.substr(0, w)
-                  )
-                });
-                return true;
-              }
-              return false;
-            });
-          });
-        });
-        return ret;
-      }
-    ]
-  });
-
-  var user_input;
-  while (typeof user_input == 'boolean' || typeof user_input == 'undefined') {
-    user_input = typeahead.inkey(console.inkey(K_NONE));
-    typeahead.cycle();
-    if (frame.cycle()) {
-      console.gotoxy(console.screen_columns, console.screen_rows);
-      typeahead.updateCursor();
-    }
-    yield();
-  }
-  typeahead.close();
-  sframe.close();
-
-  if (typeof user_input == 'object') {
-    node_info_popup(user_input.node, frame, settings);
-  }
+	const sframe = new Frame(frame.x, frame.y + 4, frame.width, 3, BG_BLUE|WHITE, frame);
+	sframe.putmsg('Search by address, system name, sysop, or location:');
+	sframe.gotoxy(1, 3);
+	sframe.putmsg("Type a query and wait for results, don't hit enter.");
+	sframe.open();
+	const typeahead = new Typeahead({
+		x: 1,
+		y: sframe.y + 1,
+		bg: 0,
+		prompt: '',
+		frame: sframe,
+		datasources: [
+			function (str) {
+				const w = (Math.floor((frame.width - 9 - 15) / 3) - 1);
+				const ret = [];
+				const ftn_domains = new FTNDomains();
+				const files = Object.keys(ftn_domains.nodeListFN).map(function (e) {
+				    return ftn_domains.nodeListFN[e];
+				});
+				Object.keys(settings).forEach(function (e) {
+					if (e.search(/^nodelist_/) > -1) files.push(settings[e]);
+				});
+				files.forEach(function (e) {
+					if (!file_exists(e)) return
+					const nodelist = new NodeList(e);
+					nodelist.entries.forEach(function (e) {
+						var r = ['addr', 'sysop', 'name', 'location'].some(function (ee) {
+							if (e[ee].search(new RegExp(str, 'i')) > -1) {
+								ret.push({
+									node: e,
+									text: format(
+										'%-15s%-' + w + 's %-' + w + 's %-' + w + 's',
+										e.addr, e.name.substr(0, w), e.location.substr(0, w), e.sysop.substr(0, w)
+									),
+								});
+								return true;
+							}
+							return false;
+						});
+			    	});
+				});
+				return ret;
+			}
+		],
+	});
+	var user_input;
+	while (typeof user_input == 'boolean' || typeof user_input == 'undefined') {
+		user_input = typeahead.inkey(console.inkey(K_NONE));
+		typeahead.cycle();
+		if (frame.cycle()) {
+			console.gotoxy(console.screen_columns, console.screen_rows);
+			typeahead.updateCursor();
+		}
+		yield();
+	}
+	typeahead.close();
+	sframe.close();
+	if (typeof user_input == 'object') {
+		node_info_popup(user_input.node, frame, settings);
+	}
 
 }
 
 function main() {
-
-  const ca = console.attributes;
-  const bss = bbs.sys_status;
-  bbs.sys_status|=SS_MOFF;
-
-  const frame = new Frame(
-    1, 1, console.screen_columns, console.screen_rows, BG_BLUE|WHITE
-  );
-  const tree_frame = new Frame(
-    frame.x, frame.y + 2, frame.width, frame.height - 4, BG_BLACK, frame
-  );
-  const tree = new Tree(tree_frame);
-  tree.colors.lfg = WHITE;
-  tree.colors.lbg = BG_CYAN;
-  tree.colors.tfg = LIGHTCYAN;
-  tree.colors.kfg = WHITE;
-  tree.colors.xfg = LIGHTCYAN;
-  const scrollbar = new ScrollBar(tree, {autohide : true});
-
-  // Valid settings:
-  // auto_close_domain t/f
-  // auto_close_zone t/f
-  // auto_close_net t/f
-  // nodelist_xxx /path/to/some/nodelist
-  const defaults = {
-    auto_close_domain: true,
-    auto_close_zone: true,
-    auto_close_net: true
-  };
-  const settings = load({}, 'modopts.js', 'fido_nodelist_browser') || {};
-  Object.keys(defaults).forEach(function (e) {
-    if (typeof settings[e] == 'undefined') settings[e] = defaults[e];
-  });
-  settings.frame = frame;
-
-  frame.putmsg('Nodelist Browser');
-  frame.gotoxy(1, frame.height);
-  frame.putmsg('\1h\1cS\1h\1wearch  \1h\1cQ\1h\1wuit');
-  populate_tree(tree, settings);
-
-  console.clear(LIGHTGRAY);
-  frame.open();
-  tree.open();
-  frame.cycle();
-  scrollbar.cycle();
-
-  var user_input;
-  while ((user_input = console.getkey(K_NOSPIN).toLowerCase()) != 'q') {
-    if (user_input == 's') {
-      search(frame, settings);
-    } else {
-      tree.getcmd(user_input);
-      scrollbar.cycle();
-    }
-    if (frame.cycle()) {
-      console.gotoxy(console.screen_columns, console.screen_rows);
-    }
-    yield();
-  }
-
-  frame.close();
-  bbs.sys_status = bss;
-  console.clear(ca);
-
+	const ca = console.attributes;
+	const bss = bbs.sys_status;
+	bbs.sys_status|=SS_MOFF;
+	const frame = new Frame(1, 1, console.screen_columns, console.screen_rows, BG_BLUE|WHITE);
+	const tree_frame = new Frame(frame.x, frame.y + 2, frame.width, frame.height - 4, BG_BLACK, frame);
+	const tree = new Tree(tree_frame);
+	tree.colors.lfg = WHITE;
+	tree.colors.lbg = BG_CYAN;
+	tree.colors.tfg = LIGHTCYAN;
+	tree.colors.kfg = WHITE;
+	tree.colors.xfg = LIGHTCYAN;
+	const scrollbar = new ScrollBar(tree, { autohide: true });
+	// Valid settings:
+	// auto_close_domain t/f
+	// auto_close_zone t/f
+	// auto_close_net t/f
+	// nodelist_xxx /path/to/some/nodelist
+	const defaults = {
+		auto_close_domain: true,
+		auto_close_zone: true,
+		auto_close_net: true
+	};
+	const settings = load({}, 'modopts.js', 'fido_nodelist_browser') || {};
+	Object.keys(defaults).forEach(function (e) {
+		if (typeof settings[e] == 'undefined') settings[e] = defaults[e];
+	});
+	settings.frame = frame;
+	frame.putmsg('Nodelist Browser');
+	frame.gotoxy(1, frame.height);
+	frame.putmsg('\1h\1cS\1h\1wearch  \1h\1cQ\1h\1wuit');
+	populate_tree(tree, settings, frame);
+	console.clear(LIGHTGRAY);
+	frame.open();
+	tree.open();
+	frame.cycle();
+	scrollbar.cycle();
+	var user_input;
+	while ((user_input = console.getkey(K_NOSPIN).toLowerCase()) != 'q') {
+		if (user_input == 's') {
+			search(frame, settings);
+		} else {
+			tree.getcmd(user_input);
+			scrollbar.cycle();
+		}
+		if (frame.cycle()) console.gotoxy(console.screen_columns, console.screen_rows);
+	}
+	frame.close();
+	bbs.sys_status = bss;
+	console.clear(ca);
 }
 
 main();