diff --git a/exec/load/dd_lightbar_menu.js b/exec/load/dd_lightbar_menu.js
index 8d348ef36b4e12ccbfcdc10b39d4deeecb51ff24..2511b68467714bedccc7b1bd271865741c19fea7 100644
--- a/exec/load/dd_lightbar_menu.js
+++ b/exec/load/dd_lightbar_menu.js
@@ -241,18 +241,9 @@ else
 var KEY_ESC = ascii(27);
 var KEY_ENTER = "\x0d";
 // PageUp & PageDown keys - Synchronet 3.17 as of about December 18, 2017
-// use CTRL-P and CTRL-N for PageUp and PageDown, respectively.  sbbsdefs.js
-// defines them as KEY_PAGEUP and KEY_PAGEDN; I've used slightly different names
-// in this script so that this script will work with Synchronet systems before
-// and after the update containing those key definitions.
-var KEY_PAGE_UP = "\x10"; // Ctrl-P
-var KEY_PAGE_DOWN = "\x0e"; // Ctrl-N
-// Ensure KEY_PAGE_UP and KEY_PAGE_DOWN are set to what's defined in sbbs.js
-// for KEY_PAGEUP and KEY_PAGEDN in case they change
-if (typeof(KEY_PAGEUP) === "string")
-	KEY_PAGE_UP = KEY_PAGEUP;
-if (typeof(KEY_PAGEDN) === "string")
-	KEY_PAGE_DOWN = KEY_PAGEDN;
+// use CTRL-P and CTRL-N for PageUp and PageDown, respectively.  key_defs.js
+// defines them as KEY_PAGEUP and KEY_PAGEDN (key_defs.js is loaded by
+// sbbsdefs.js).
 
 // Box-drawing/border characters: Single-line
 var UPPER_LEFT_SINGLE = "\xDA";
@@ -390,6 +381,7 @@ function DDLightbarMenu(pX, pY, pWidth, pHeight)
 	this.Draw = DDLightbarMenu_Draw;
 	this.DrawBorder = DDLightbarMenu_DrawBorder;
 	this.WriteItem = DDLightbarMenu_WriteItem;
+	this.GetItemText = DDLightbarMenu_GetItemText;
 	this.Erase = DDLightbarMenu_Erase;
 	this.SetItemHotkey = DDLightbarMenu_SetItemHotkey;
 	this.AddItemHotkey = DDLightbarMenu_AddItemHotkey;
@@ -765,6 +757,37 @@ function DDLightbarMenu_DrawBorder()
 //  pScreenY: Optional - The vertical screen coordinate of the start of the item
 function DDLightbarMenu_WriteItem(pIdx, pItemLen, pHighlight, pSelected, pScreenX, pScreenY)
 {
+	var itemText = this.GetItemText(pIdx, pItemLen, pHighlight, pSelected);
+	// If this.nextDrawOnlyItemSubstr is an object with start & end properties,
+	// then create a string that is shortened from itemText from those start & end
+	// indexes, and add color to it.
+	// Otherwise, just print the full item text.
+	if ((this.nextDrawOnlyItemSubstr != null) && (typeof(this.nextDrawOnlyItemSubstr) == "object") && this.nextDrawOnlyItemSubstr.hasOwnProperty("start") && this.nextDrawOnlyItemSubstr.hasOwnProperty("end") && (typeof(pScreenX) == "number") && (typeof(pScreenY) == "number"))
+	{
+		var len = this.nextDrawOnlyItemSubstr.end - this.nextDrawOnlyItemSubstr.start;
+		var shortenedText = substrWithAttrCodes(itemText, this.nextDrawOnlyItemSubstr.start, len);
+		console.gotoxy(pScreenX+this.nextDrawOnlyItemSubstr.start, pScreenY);
+		console.print(shortenedText + "\1n");
+	}
+	else
+		console.print(itemText + "\1n");
+}
+
+// Gets the text of a menu item with colors applied
+//
+// Parameters:
+//  pIdx: The index of the item to get
+//  pItemLen: Optional - Calculated length of the item (in case the scrollbar is showing).
+//            If this is not given, then this will be calculated.
+//  pHighlight: Optional - Whether or not to highlight the item.  If this is not given,
+//              the item will be highlighted based on whether the current selected item
+//              matches the given index, pIdx.
+//  pSelected: Optional - Whether or not this item is selected (mainly intended for multi-select
+//             mode).  Defaults to false.  If true, then a mark character will be displayed
+//             at the end of the item's text.
+function DDLightbarMenu_GetItemText(pIdx, pItemLen, pHighlight, pSelected)
+{
+	var itemText = "";
 	var numItems = this.NumItems();
 	if ((pIdx >= 0) && (pIdx < numItems))
 	{
@@ -815,7 +838,7 @@ function DDLightbarMenu_WriteItem(pIdx, pItemLen, pHighlight, pSelected, pScreen
 		// Use strip_ctrl to ensure there are no attribute codes, since we will
 		// apply our own.  This might be only a temporary item returned by a
 		// replaced GetItem(), so we just have to strip_ctrl() it here.
-		var itemText = strip_ctrl(menuItem.text);
+		itemText = strip_ctrl(menuItem.text);
 		if (itemTextDisplayableLen(itemText, this.ampersandHotkeysInItems) > itemLen)
 			itemText = itemText.substr(0, itemLen);
 		// Add the item color to the item text
@@ -866,20 +889,8 @@ function DDLightbarMenu_WriteItem(pIdx, pItemLen, pHighlight, pSelected, pScreen
 		// If in numbered mode, add the item number to the front of the item text.
 		if (this.numberedMode)
 			itemText = format("\1n%" + this.itemNumLen + "d ", pIdx+1) + itemText;
-		// If this.nextDrawOnlyItemSubstr is an object with start & end properties,
-		// then create a string that is shortened from itemText from those start & end
-		// indexes, and add color to it.
-		// Otherwise, just print the full item text.
-		if ((this.nextDrawOnlyItemSubstr != null) && (typeof(this.nextDrawOnlyItemSubstr) == "object") && this.nextDrawOnlyItemSubstr.hasOwnProperty("start") && this.nextDrawOnlyItemSubstr.hasOwnProperty("end") && (typeof(pScreenX) == "number") && (typeof(pScreenY) == "number"))
-		{
-			var len = this.nextDrawOnlyItemSubstr.end - this.nextDrawOnlyItemSubstr.start;
-			var shortenedText = substrWithAttrCodes(itemText, this.nextDrawOnlyItemSubstr.start, len);
-			console.gotoxy(pScreenX+this.nextDrawOnlyItemSubstr.start, pScreenY);
-			console.print(shortenedText + "\1n");
-		}
-		else
-			console.print(itemText + "\1n");
 	}
+	return itemText;
 }
 
 // Erases the menu - Draws black (normal color) where the menu was
@@ -983,7 +994,7 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
 		if (this.scrollbarEnabled && !this.CanShowAllItemsInWindow())
 			this.UpdateScrollbarWithHighlightedItem();
 
-		this.lastUserInput = getKeyWithESCChars(K_NOECHO|K_NOSPIN|K_NOCRLF);
+		this.lastUserInput = console.getkey(K_NOECHO|K_NOSPIN|K_NOCRLF);
 		if ((this.lastUserInput == KEY_ESC) || (this.QuitKeysIncludes(this.lastUserInput)))
 		{
 			continueOn = false;
@@ -1117,7 +1128,7 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
 				}
 			}
 		}
-		else if (this.lastUserInput == KEY_PAGE_UP)
+		else if (this.lastUserInput == KEY_PAGEUP)
 		{
 			// Only do this if we're not already at the top of the list
 			if (this.topItemIdx > 0)
@@ -1153,7 +1164,7 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
 				}
 			}
 		}
-		else if (this.lastUserInput == KEY_PAGE_DOWN)
+		else if (this.lastUserInput == KEY_PAGEDN)
 		{
 			var numItemsPerPage = (this.borderEnabled ? this.size.height - 2 : this.size.height);
 			// Only do the pageDown if we're not showing the last item already
@@ -1313,21 +1324,21 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
 				console.gotoxy(XPos, YPos);
 				if (added)
 				{
-					var itemColor = this.GetColorForItem(this.selectedItemIdx);
 					// If the item color is an array, then default to a color string here
+					var itemColor = this.GetColorForItem(this.selectedItemIdx, true);
 					if (Array.isArray(itemColor))
-						itemColor = "\1n\1h\1g";
+					{
+						var bkgColor = getBackgroundAttrAtIdx(itemColor, this.size.width-1);
+						itemColor = "\1n\1h\1g" + bkgColor;
+					}
 					console.print(itemColor + " " + this.multiSelectItemChar + "\1n");
 				}
 				else
 				{
-					// If any of the item text is right at the end, then display it.  Otherwise,
-					// display 2 spaces.
-					var textToPrint = "  ";
-					var theItem = this.GetItem(this.selectedItemIdx);
-					if (theItem.text.length >= this.size.width)
-						textToPrint = theItem.text.substr(this.size.width-2, 2);
-					console.print(this.colors.selectedItemColor + textToPrint + "\1n");
+					// Display the last 2 characters of the regular item text
+					var itemText = this.GetItemText(this.selectedItemIdx, null, true, false);
+					var textToPrint = substrWithAttrCodes(itemText, console.strlen(itemText)-2, 2);
+					console.print(textToPrint + "\1n");
 				}
 			}
 		}
@@ -1846,14 +1857,19 @@ function DDLightbarMenu_ItemUsesAltColors(pItemIndex)
 //
 // Parameters:
 //  pItemIndex: The index of the item
+//  pSelected: Whether or not to use selected item colors.  Defaults to false.
 //
 // Return value: Either colors.itemColor or colors.altItemColor
-function DDLightbarMenu_GetColorForItem(pItemIndex)
+function DDLightbarMenu_GetColorForItem(pItemIndex, pSelected)
 {
 	if ((pItemIndex < 0) || (pItemIndex >= this.NumItems()))
 		return "";
 
-	return (this.GetItem(pItemIndex).useAltColors ? this.colors.altItemColor : this.colors.itemColor);
+	var selected = (typeof(pSelected) == "boolean" ? pSelected : false);
+	if (selected)
+		return (this.GetItem(pItemIndex).useAltColors ? this.colors.altSelectedItemColor : this.colors.selectedItemColor);
+	else
+		return (this.GetItem(pItemIndex).useAltColors ? this.colors.altItemColor : this.colors.itemColor);
 }
 
 // Returns either the selected or alternate selected color for an item
@@ -1901,65 +1917,6 @@ function DDLightbarMenu_CalcScrollbarBlocks()
 //////////////////////////////////////////////////////////
 // Helper functions, not part of the DDLightbarMenu class
 
-// Inputs a keypress from the user and handles some ESC-based
-// characters such as PageUp, PageDown, and ESC.  If PageUp
-// or PageDown are pressed, this function will return the
-// string "\1PgUp" (KEY_PAGE_UP) or "\1Pgdn" (KEY_PAGE_DOWN),
-// respectively.  Also, F1-F5 will be returned as "\1F1"
-// through "\1F5", respectively.
-// Thanks goes to Psi-Jack for the original impementation
-// of this function.
-//
-// Parameters:
-//  pGetKeyMode: Optional - The mode bits for console.getkey().
-//               If not specified, K_NONE will be used.
-//
-// Return value: The user's keypress
-function getKeyWithESCChars(pGetKeyMode)
-{
-   var getKeyMode = K_NONE;
-   if (typeof(pGetKeyMode) == "number")
-      getKeyMode = pGetKeyMode;
-
-   var userInput = console.getkey(getKeyMode);
-   if (userInput == KEY_ESC) {
-      switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
-         case '[':
-            switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
-               case 'V':
-                  userInput = KEY_PAGE_UP;
-                  break;
-               case 'U':
-                  userInput = KEY_PAGE_DOWN;
-                  break;
-           }
-           break;
-         case 'O':
-           switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
-              case 'P':
-                 userInput = "\1F1";
-                 break;
-              case 'Q':
-                 userInput = "\1F2";
-                 break;
-              case 'R':
-                 userInput = "\1F3";
-                 break;
-              case 'S':
-                 userInput = "\1F4";
-                 break;
-              case 't':
-                 userInput = "\1F5";
-                 break;
-           }
-         default:
-           break;
-      }
-   }
-
-   return userInput;
-}
-
 // Returns the length of an item's text, not counting non-displayable
 // characters (such as Synchronet color attributes and an ampersand
 // immediately before a non-space)
@@ -2043,6 +2000,32 @@ function shortenStrWithAttrCodes(pStr, pNewLength, pFromLeft)
 	return strCopy;
 }
 
+// Returns whether or not all string attribute objects in an array have the
+// expected properties, and that the property types are correct, for menu item
+// string color definitions.
+//
+// Parameters:
+//  pAttrsArray: An array of objects which are expected to containg the
+//               following properties: start, end, attrs
+//
+// Return value: Boolean - Whether or not all elements in the array
+//               have all the expected properties
+function attrsArrayElementsHaveAllCorrectProps(pAttrsArray)
+{
+	var allElementsHaveCorrectProps = true;
+	for (var i = 0; (i < pAttrsArray.length) && allElementsHaveCorrectProps; ++i)
+	{
+		allElementsHaveCorrectProps = ((typeof(pAttrsArray[i]) == "object") &&
+		                               pAttrsArray[i].hasOwnProperty("start") &&
+		                               pAttrsArray[i].hasOwnProperty("end") &&
+		                               pAttrsArray[i].hasOwnProperty("attrs") &&
+		                               (typeof(pAttrsArray[i].start) == "number") &&
+		                               (typeof(pAttrsArray[i].end) == "number") &&
+		                               (typeof(pAttrsArray[i].attrs) == "string"));
+	}
+	return allElementsHaveCorrectProps;
+}
+
 // Adds color/attribute codes to a string.
 //
 // Parameters:
@@ -2067,23 +2050,10 @@ function addAttrsToString(pStr, pAttrs)
 	var str;
 	if (Array.isArray(pAttrs))
 	{
-		if (pAttrs.length > 0)
+		// To use the attributes array, the array must have some objects and
+		// each element of the array must have start, end, and attrs properties
+		if ((pAttrs.length > 0) && attrsArrayElementsHaveAllCorrectProps(pAttrs))
 		{
-			// Ensure each element of the array has start, end, and attrs properties
-			var allElementsHaveCorrectProps = true;
-			for (var i = 0; (i < pAttrs.length) && allElementsHaveCorrectProps; ++i)
-			{
-				allElementsHaveCorrectProps = ((typeof(pAttrs[i]) == "object") &&
-				                               pAttrs[i].hasOwnProperty("start") &&
-				                               pAttrs[i].hasOwnProperty("end") &&
-				                               pAttrs[i].hasOwnProperty("attrs") &&
-				                               (typeof(pAttrs[i].start) == "number") &&
-				                               (typeof(pAttrs[i].end) == "number") &&
-				                               (typeof(pAttrs[i].attrs) == "string"));
-			}
-			if (!allElementsHaveCorrectProps)
-				return pStr;
-
 			// Colorize the string with the object in pAttrs.
 			// Don't do the last object in this loop, because for the last object,
 			// we'll want to check if its end index is valid.
@@ -2125,6 +2095,74 @@ function addAttrsToString(pStr, pAttrs)
 	return str;
 }
 
+function getBackgroundAttrAtIdx(pAttrs, pIdx)
+{
+	if (typeof(pIdx) != "number")
+		return "";
+	if (pIdx < 0)
+		return "";
+
+	// Synchronet background color codes:
+	// Black: 0
+	// Red: 1
+	// Green: 2
+	// Yellow/brown: 3
+	// Blue: 4
+	// Magenta: 5
+	// Cyan: 6
+	// White/grey: 7
+	var syncBkgAttrRegex = /\1[01234567]/;
+	var bkgAttr = "";
+	if (Array.isArray(pAttrs))
+	{
+		if ((pAttrs.length > 0) && attrsArrayElementsHaveAllCorrectProps(pAttrs))
+		{
+			// Go through the array, and if a start & end is found where pIdx
+			// falls between, check that objects attrs property for its last
+			// background attribute, if there is one
+			for (var i = 0; i < pAttrs.length; ++i)
+			{
+				if ((pIdx >= pAttrs[i].start) && ((pIdx < pAttrs[i].end) || (pAttrs[i].end == 0)))
+				{
+					// Check the attrs for the last background attribute, starting
+					// from the end
+					if (pAttrs[i].attrs.length >= 2)
+					{
+						for (var attrIdx = pAttrs[i].attrs.length - 2; attrIdx >= 0; attrIdx -= 2)
+						{
+							var currentTwo = pAttrs[i].attrs.substr(attrIdx, 2);
+							if (syncBkgAttrRegex.test(currentTwo))
+							{
+								bkgAttr = currentTwo;
+								break;
+							}
+						}
+					}
+					break;
+				}
+			}
+		}
+	}
+	else if (typeof(pAttrs) == "string")
+	{
+		if ((pIdx >= 0) || (pIdx < pAttrs.length))
+		{
+			// Starting from pIdx, go backwards through pAttrs, and if a Synchronet
+			// background attribute code is found, then use it.
+			for (var i = pIdx - 2; i >= 0; i -= 2)
+			{
+				var currentTwo = pAttrs.substr(i, 2);
+				if (syncBkgAttrRegex.test(currentTwo))
+				{
+					bkgAttr = currentTwo;
+					break;
+				}
+			}
+		}
+	}
+	return bkgAttr;
+}
+
 // Returns a default item object for a DDLightbarMenu
 function getDefaultMenuItem() {
 	return {