Skip to content
Snippets Groups Projects
DDMsgReader.js 650 KiB
Newer Older
	var allowChgSubBoard = (typeof(pAllowChgSubBoard) == "boolean" ? pAllowChgSubBoard : true);
	// This function will be used for displaying the help line at
	// the bottom of the screen.
	function DisplayHelpLine(pHelpLineText)
	{
		console.gotoxy(1, console.screen_rows);
		console.print(pHelpLineText);
		console.cleartoeol("\1n");
	}
	// Clear the screen and write the header at the top
	console.clear("\1n");
	this.WriteMsgListScreenTopHeader();
	DisplayHelpLine(this.msgListLightbarModeHelpLine);

	// If the lightbar message list index & cursor position variables haven't been
	// set yet, then set them.
	if ((this.lightbarListTopMsgIdx == -1) || (this.lightbarListSelectedMsgIdx == -1) ||
	    (this.lightbarListCurPos == null))
	{
		this.SetUpLightbarMsgListVars();
	}

	// List a screenful of message headers
	console.gotoxy(1, this.lightbarMsgListStartScreenRow);
	var lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
	// Move the cursor to where it needs to be
	console.gotoxy(this.lightbarListCurPos);
	// User input loop
	var bottomMsgIndex = 0;
	var userInput = "";
	var msgHeader = null;
	var continueOn = true;
	while (continueOn)
	{
		bbs.command_str = ""; // To prevent weirdness

		retObj.selectedMsgOffset = -1;

		// Calculate the message number (0-based) of the message
		// appearing on the bottom of the screen.
			bottomMsgIndex = this.lightbarListTopMsgIdx - this.lightbarMsgListNumLines + 1;
			if (bottomMsgIndex < 0)
				bottomMsgIndex = 0;
			var totalNumMessages = this.NumMessages();
			bottomMsgIndex = this.lightbarListTopMsgIdx + this.lightbarMsgListNumLines - 1;
			if (bottomMsgIndex >= totalNumMessages)
				bottomMsgIndex = totalNumMessages - 1;
		// Write the current message information with highlighting colors
		msgHeader = this.GetMsgHdrByIdx(this.lightbarListSelectedMsgIdx);
		this.PrintMessageInfo(msgHeader, true, this.lightbarListSelectedMsgIdx+1);
		console.gotoxy(this.lightbarListCurPos); // Make sure the cursor is still in the right place
		// Get a key from the user (upper-case) and take appropriate action.
		userInput = getKeyWithESCChars(K_UPPER|K_NOCRLF|K_NOECHO|K_NOSPIN);
		retObj.lastUserInput = userInput;
		// Q: Quit
		if (userInput == "Q")
		{
			// Quit
			continueOn = false;
			break;
		}
		// ?: Show help
		else if (userInput == "?")
		{
			// Display help
			console.clear("\1n");
			this.DisplayMsgListHelp(allowChgSubBoard, true);
			// Re-draw the message list on the screen
			console.clear("\1n");
			this.WriteMsgListScreenTopHeader();
			DisplayHelpLine(this.msgListLightbarModeHelpLine);
			console.gotoxy(1, this.lightbarMsgListStartScreenRow);
			lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
			console.gotoxy(this.lightbarListCurPos); // Put the cursor back where it should be
		}
		// Up arrow: Highlight the previous message
		else if (userInput == KEY_UP)
		{
			// Make sure this.lightbarListSelectedMsgIdx is within bounds before moving down.
			{
				if (this.lightbarListSelectedMsgIdx >= this.NumMessages() - 1)
					continue;
			}
			else
			{
				if (this.lightbarListSelectedMsgIdx <= 0)
					continue;
			}
			// Print the current message information with regular colors
			this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
				++this.lightbarListSelectedMsgIdx;
			else
				--this.lightbarListSelectedMsgIdx;
			// If the current screen row is above the first line allowed, then
			// move the cursor up one row.
			if (this.lightbarListCurPos.y > this.lightbarMsgListStartScreenRow)
			{
				console.gotoxy(1, this.lightbarListCurPos.y-1);
				this.lightbarListCurPos.x = 1;
				--this.lightbarListCurPos.y;
			}
			else
			{
				// Go onto the previous page, with the cursor highlighting
				// the last message on the page.
					this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx + this.lightbarMsgListNumLines - 1;
				else
					this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx - this.lightbarMsgListNumLines + 1;
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow+this.lightbarMsgListNumLines-1);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow+this.lightbarMsgListNumLines-1;
			}
		}
		// Down arrow: Highlight the next message
		else if (userInput == KEY_DOWN)
		{
			// Make sure this.lightbarListSelectedMsgIdx is within bounds before moving down.
			{
				if (this.lightbarListSelectedMsgIdx <= 0)
					continue;
			}
			else
			{
				if (this.lightbarListSelectedMsgIdx >= this.NumMessages() - 1)
					continue;
			}
			// Print the current message information with regular colors
			this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
				--this.lightbarListSelectedMsgIdx;
			else
				++this.lightbarListSelectedMsgIdx;
			// If the current screen row is below the last line allowed, then
			// move the cursor down one row.
			if (this.lightbarListCurPos.y < this.lightbarMsgListStartScreenRow+this.lightbarMsgListNumLines-1)
			{
				console.gotoxy(1, this.lightbarListCurPos.y+1);
				this.lightbarListCurPos.x = 1;
				++this.lightbarListCurPos.y;
			}
			else
			{
				// Go onto the next page, with the cursor highlighting
				// the first message on the page.
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx;
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				// If we were on the last page, then clear the screen from
				// the current line to the end of the screen.
				if (lastPage)
				{
					this.lightbarListCurPos = console.getxy();
					clearToEOS(this.lightbarListCurPos.y);
					// Make sure the help line is still there
					DisplayHelpLine(this.msgListLightbarModeHelpLine);
				}
				// Move the cursor to the top of the list
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
			}
		}
		// HOME key: Go to the first message on the screen
		else if (userInput == KEY_HOME)
		{
			// Print the current message information with regular colors
			this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
			// Go to the first message of the current page
				this.lightbarListSelectedMsgIdx += (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
			else
				this.lightbarListSelectedMsgIdx -= (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
			// Move the cursor to the first message line
			console.gotoxy(1, this.lightbarMsgListStartScreenRow);
			this.lightbarListCurPos.x = 1;
			this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
		}
		// END key: Go to the last message on the screen
		else if (userInput == KEY_END)
		{
			// Print the current message information with regular colors
			this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
			// Update the selected message #
			this.lightbarListSelectedMsgIdx = bottomMsgIndex;
			// Go to the last message of the current page
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + this.lightbarListTopMsgIdx - bottomMsgIndex;
			else
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + bottomMsgIndex - this.lightbarListTopMsgIdx;
			console.gotoxy(this.lightbarListCurPos);
		}
		// Enter key: Select a message to read
		else if (userInput == KEY_ENTER)
		{
			var originalCurpos = console.getxy();
			// Allow the user to read the current message.
			var readMsg = true;
			if (this.promptToReadMessage)
			{
				// Confirm with the user whether to read the message.
				var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
				                        + "Read message "
				                        + this.colors["readMsgConfirmNumberColor"]
				                        + +(msgHeader.offset+1)
				                        + this.colors["readMsgConfirmColor"]
				                        + ": Are you sure";
				console.gotoxy(1, console.screen_rows);
				console.print("\1n");
				console.clearline();
				readMsg = console.yesno(sReadMsgConfirmText);
			}
			var repliedToMessage = false;
			if (readMsg)
			{
				// If there is a search specified and the search result objects are
				// set up for the current sub-board, then the selected message offset
				// should be the search result array index.  Otherwise (if not
				// searching), the message offset should be the actual message offset
				// in the message base.
				if (this.SearchingAndResultObjsDefinedForCurSub())
					retObj.selectedMsgOffset = this.lightbarListSelectedMsgIdx;
				else
					retObj.selectedMsgOffset = msgHeader.offset;
				if (pReturnOnMsgSelect)
					return retObj;
				else
				{
					this.readAMessage = true;
					console.clear("\1n");
					var readRetObj = null;
					if (this.SearchingAndResultObjsDefinedForCurSub())
						readRetObj = this.ReadMessage(this.lightbarListSelectedMsgIdx);
						readRetObj = this.ReadMessage(msgHeader.offset);
					repliedToMessage = readRetObj.userReplied;
				}
			}
			else
				this.deniedReadingMessage = true;
			// Ask the user if  they want to continue reading messages
			if (this.promptToContinueListingMessages)
			{
				continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +
				"Continue listing messages");
			}
			// If the user chose to continue reading messages, then refresh
			// the screen.  Even if the user chooses not to read the message,
			// the screen needs to be re-drawn so it appears properly.
			if (continueOn)
			{
				console.clear("\1n");
				this.WriteMsgListScreenTopHeader();
				DisplayHelpLine(this.msgListLightbarModeHelpLine);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				// If we're dispaying in reverse order and the user replied
				// to the message, then we'll have to re-arrange the screen
				// a bit to make way for the new message that will appear
				// in the list.
				if (this.reverseListOrder && repliedToMessage)
				{
					// Make way for the new message, which will appear at the
					// top.
					++this.lightbarListTopMsgIdx;
					// If the cursor is below the bottommost line displaying
					// messages, then advance the cursor down one position.
					// Otherwise, increment this.lightbarListSelectedMsgIdx (since a new message
					// will appear at the top, the previous selected message
					// will be pushed to the next page).
					if (this.lightbarListCurPos.y < console.screen_rows - 1)
					{
						++originalCurpos.y;
						++this.lightbarListCurPos.y;
					}
					else
						++this.lightbarListSelectedMsgIdx;
				}
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(originalCurpos); // Put the cursor back where it should be
			}
		}
		// PageDown: Next page
		else if (userInput == KEY_PAGE_DOWN)
		{
			// Next page
			if (!lastPage)
			{
					this.lightbarListTopMsgIdx -= this.lightbarMsgListNumLines;
				else
					this.lightbarListTopMsgIdx += this.lightbarMsgListNumLines;
				this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);

				// If we were on the last page, then clear the screen from
				// the current line to the end of the screen.
				if (lastPage)
				{
					this.lightbarListCurPos = console.getxy();
					clearToEOS(this.lightbarListCurPos.y);
					// Make sure the help line is still there
					DisplayHelpLine(this.msgListLightbarModeHelpLine);
				}
				// Move the cursor back to the first message info line
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
			}
			else {
				// The user is on the last page - Go to the last message on the page.
				if (this.lightbarListSelectedMsgIdx != bottomMsgIndex)
				{
					// Print the current message information with regular colors
					this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
					// Update the selected message #
					this.lightbarListSelectedMsgIdx = bottomMsgIndex;
					this.lightbarListCurPos.x = 1;
					if (this.reverseListOrder)
						this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + this.lightbarListTopMsgIdx - bottomMsgIndex;
					else
						this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + bottomMsgIndex - this.lightbarListTopMsgIdx;
					console.gotoxy(this.lightbarListCurPos);
				}
			}
		}
		// PageUp: Previous page
		else if (userInput == KEY_PAGE_UP)
		{
			var canGoToPrevious = false;
				canGoToPrevious = (this.lightbarListTopMsgIdx < this.NumMessages() - 1);
			else
				canGoToPrevious = (this.lightbarListTopMsgIdx > 0);
					this.lightbarListTopMsgIdx += this.lightbarMsgListNumLines;
				else
					this.lightbarListTopMsgIdx -= this.lightbarMsgListNumLines;
				this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
			}
			else
			{
				// The user is on the first page - Go to the first message on the page.
				if (this.lightbarListSelectedMsgIdx != 0)
				{
					// Print the current message information with regular colors
					this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
					// Go to the first message of the current page
					if (this.reverseListOrder)
						this.lightbarListSelectedMsgIdx += (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
					else
						this.lightbarListSelectedMsgIdx -= (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
					// Move the cursor to the first message line
					console.gotoxy(1, this.lightbarMsgListStartScreenRow);
					this.lightbarListCurPos.x = 1;
					this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
				}
			}
		}
		// F: First page
		else if (userInput == "F")
		{
			var canGoToFirst = false;
				canGoToFirst = (this.lightbarListTopMsgIdx < this.NumMessages() - 1);
			else
				canGoToFirst = (this.lightbarListTopMsgIdx > 0);

			if (canGoToFirst)
			{
					this.lightbarListTopMsgIdx = this.NumMessages() - 1;
				else
					this.lightbarListTopMsgIdx = 0;
				this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
			}
		}
		// L: Last page
		else if (userInput == "L")
		{
			if (!lastPage)
			{
				// Set the top message index.  If this.lightbarListTopMsgIdx is beyond the last
				// message in the sub-board, then move back a full page of messages.
				{
					this.lightbarListTopMsgIdx = (this.NumMessages() % this.lightbarMsgListNumLines) - 1;
					// If this.lightbarListTopMsgIdx is now invalid (below 0), then adjust it
					// to properly display the last page of messages.
					if (this.lightbarListTopMsgIdx < 0)
						this.lightbarListTopMsgIdx = this.lightbarMsgListNumLines - 1;
				}
				else
				{
					var totalNumMessages = this.NumMessages();
					this.lightbarListTopMsgIdx = totalNumMessages - (totalNumMessages % this.lightbarMsgListNumLines);
					if (this.lightbarListTopMsgIdx >= totalNumMessages)
						this.lightbarListTopMsgIdx = totalNumMessages - this.lightbarMsgListNumLines;
				}
				this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				// If we were on the last page, then clear the screen from
				// the current line to the end of the screen.
				if (lastPage)
				{
					this.lightbarListCurPos = console.getxy();
					clearToEOS(this.lightbarListCurPos.y);
					// Make sure the help line is still there
					DisplayHelpLine(this.msgListLightbarModeHelpLine);
				}
				// Move the cursor back to the first message info line
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				this.lightbarListCurPos.x = 1;
				this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
			}
		}
		// Numeric digit: The start of a number of a message to read
		else if (userInput.match(/[0-9]/))
		{
			var originalCurpos = console.getxy();
			// Put the user's input back in the input buffer to
			// be used for getting the rest of the message number.
			console.ungetstr(userInput);
			// Move the cursor to the bottom of the screen and
			// prompt the user for the message number.
			console.gotoxy(1, console.screen_rows);
			userInput = this.PromptForMsgNum({ x: 1, y: console.screen_rows }, this.text.readMsgNumPromptText, true, ERROR_PAUSE_WAIT_MS, false);
			if (userInput > 0)
			{
				// Confirm with the user whether to read the message
				var readMsg = true;
				if (this.promptToReadMessage)
				{
					var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
					                        + "Read message "
					                        + this.colors["readMsgConfirmNumberColor"]
					                        + userInput + this.colors["readMsgConfirmColor"]
					                        + ": Are you sure";
					readMsg = console.yesno(sReadMsgConfirmText);
				}
				if (readMsg)
				{
					// Update the message list screen variables
					this.CalcMsgListScreenIdxVarsFromMsgNum(+userInput);
					// Let the user read the message
					retObj.selectedMsgOffset = userInput - 1;
					if (pReturnOnMsgSelect)
						return retObj;
					else
					{
						this.readAMessage = true;
						if (this.SearchingAndResultObjsDefinedForCurSub())
							this.ReadMessage(this.lightbarListSelectedMsgIdx);
				// Prompt the user whether or not to continue listing
				// messages.
				if (this.promptToContinueListingMessages)
				{
					continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +
					                           "Continue listing messages");
				}
			}
			// If the user chose to continue listing messages, then re-draw
			// the screen.
			if (continueOn)
			{
				console.clear("\1n");
				this.WriteMsgListScreenTopHeader();
				DisplayHelpLine(this.msgListLightbarModeHelpLine);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(originalCurpos); // Put the cursor back where it should be
			}
		}
		// DEL key: Delete a message
		else if (userInput == KEY_DEL)
		{
			if (this.CanDelete() || this.CanDeleteLastMsg())
			{
				var originalCurpos = console.getxy();
				console.gotoxy(1, console.screen_rows);
				console.print("\1n");
				console.clearline();

				// The DeleteMessage() methdo will prompt the user for confirmation
				// to delete the message and then delete it if confirmed.
				this.DeleteMessage(this.lightbarListSelectedMsgIdx, { x: 1, y: console.screen_rows});

				// Refresh the screen
				console.clear("\1n");
				this.WriteMsgListScreenTopHeader();
				DisplayHelpLine(this.msgListLightbarModeHelpLine);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(originalCurpos); // Put the cursor back where it should be
			}
		}
		// E: Edit a message
		else if (userInput == "E")
		{
			if (this.CanEdit())
			{
				var originalCurpos = console.getxy();
				// Ask the user if they really want to edit the message
				console.gotoxy(1, console.screen_rows);
				console.print("\1n");
				console.clearline();
				// Let the user edit the message
				//var returnObj = this.EditExistingMsg(msgHeader.offset);
				var returnObj = this.EditExistingMsg(this.lightbarListSelectedMsgIdx);
				// Refresh the screen
				console.clear("\1n");
				this.WriteMsgListScreenTopHeader();
				DisplayHelpLine(this.msgListLightbarModeHelpLine);
				console.gotoxy(1, this.lightbarMsgListStartScreenRow);
				lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
				console.gotoxy(originalCurpos); // Put the cursor back where it should be
			}
		}
		// G: Go to a specific message by # (highlight or place that message on the top)
		else if (userInput == "G")
		{
			var originalCurpos = console.getxy();

			// Move the cursor to the bottom of the screen and
			// prompt the user for a message number.
			console.gotoxy(1, console.screen_rows);
			userInput = this.PromptForMsgNum({ x: 1, y: console.screen_rows }, "\n" + this.text.goToMsgNumPromptText, true, ERROR_PAUSE_WAIT_MS, false);
			if (userInput > 0)
			{
				// Make sure the message number is for a valid message (i.e., it
				// could be an invalid message number if there is a search, where
				// not all message numbers are consecutive).
				if (this.GetMsgHdrByMsgNum(userInput) != null)
				{
					// If the message is on the current page, then just go to and
					// highlight it.  Otherwise, set the user's selected message on the
					// top of the page.  We also have to make sure that this.lightbarListCurPos.y and
					// originalCurpos.y are set correctly.  Also, account for search
					// results if there are any (we'll need to have the correct array
					// index for the search results).
					var chosenMsgIndex = userInput - 1;
					if ((chosenMsgIndex <= bottomMsgIndex) && (chosenMsgIndex >= this.lightbarListTopMsgIdx))
					{
						this.lightbarListSelectedMsgIdx = chosenMsgIndex;
						originalCurpos.y = this.lightbarListCurPos.y = this.lightbarListSelectedMsgIdx - this.lightbarListTopMsgIdx + this.lightbarMsgListStartScreenRow;
					}
					else
					{
						this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx = chosenMsgIndex;
						originalCurpos.y = this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
					}
				}
				else
				{
					// The user entered an invalid message number
					console.print("\1n" + this.text.invalidMsgNumText.replace("%d", userInput) + "\1n");
					console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
				}
			// Clear & re-draw the screen, to fix any possible alignment problems
			// caused by newline output after the user inputs their choice.
			console.clear("\1n");
			this.WriteMsgListScreenTopHeader();
			DisplayHelpLine(this.msgListLightbarModeHelpLine);
			console.gotoxy(1, this.lightbarMsgListStartScreenRow);
			lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
			console.gotoxy(originalCurpos); // Put the cursor back where it should be
		}
		// C: Change to another message area (sub-board)
		else if (userInput == "C")
		{
			if (allowChgSubBoard && (this.subBoardCode != "mail"))
			{
				// Store the current sub-board code so we can see if it changed
				var oldSubCode = bbs.cursub_code;
				// Let the user choose another message area.  If they chose
				// a different message area, then set up the message base
				// object accordingly.
				this.SelectMsgArea();
				if (bbs.cursub_code != oldSubCode)
				{
					var chgSubRetval = this.ChangeSubBoard(bbs.cursub_code);
					continueOn = chgSubRetval.succeeded;
				}
				// Update the lightbar list variables and refresh the screen
				if (continueOn)
				{
					this.SetUpLightbarMsgListVars();
					console.clear("\1n");
					this.WriteMsgListScreenTopHeader();
					DisplayHelpLine(this.msgListLightbarModeHelpLine);
					// List a screenful of message headers
					console.gotoxy(1, this.lightbarMsgListStartScreenRow);
					var lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
					// Move the cursor to where it needs to be
					console.gotoxy(this.lightbarListCurPos);
				}
			}
		}
	}
}
// For the DigDistMsgListerClass: Prints a line of information about
// a message.
//
// Parameters:
//  pMsgHeader: The message header object, returned by MsgBase.get_msg_header().
//  pHighlight: Optional boolean - Whether or not to highlight the line or
//              use the standard colors.
//  pMsgNum: Optional - A number to use for the message instead of the number/offset
//           in the message header
function DigDistMsgReader_PrintMessageInfo(pMsgHeader, pHighlight, pMsgNum)
{
	// pMsgHeader must be a valid object.
	if (typeof(pMsgHeader) == "undefined")
		return;
	if (pMsgHeader == null)
		return;

	var highlight = false;
	if (typeof(pHighlight) != "undefined")
		highlight = pHighlight;

   // Determine if the message has been deleted.
   var msgDeleted = ((pMsgHeader.attr & MSG_DELETE) == MSG_DELETE);

   // Get the message's import date & time as strings.  If
   // this.msgList_displayMessageDateImported is true, use the message import date.
   // Otherwise, use the message written date.
   var sDate;
   var sTime;
   if (this.msgList_displayMessageDateImported)
   {
      sDate = strftime("%Y-%m-%d", pMsgHeader.when_imported_time);
      sTime = strftime("%H:%M:%S", pMsgHeader.when_imported_time);
   }
   else
   {
      sDate = strftime("%Y-%m-%d", pMsgHeader.when_written_time);
      sTime = strftime("%H:%M:%S", pMsgHeader.when_written_time);
   }

	// Write the message header information.
	// Note: The message header has the following fields:
	// 'number': The message number
	// 'offset': The message offset
	// 'to': Who the message is directed to (string)
	// 'from' Who wrote the message (string)
	// 'subject': The message subject (string)
	// 'date': The date - Full text (string)
	// To access one of these, use brackets; i.e., msgHeader['to']
	var msgNum = (typeof(pMsgNum) == "number" ? pMsgNum : pMsgHeader.offset+1);
	if (highlight)
	{
		printf(this.sMsgInfoFormatHighlightStr,
			   msgNum,
			   (msgDeleted ? "\1r\1i*\1n\1h" + this.colors["msgListHighlightBkgColor"] : " "),
			   pMsgHeader.from.substr(0, this.FROM_LEN),
			   pMsgHeader.to.substr(0, this.TO_LEN),
			   pMsgHeader.subject.substr(0, this.SUBJ_LEN),
			   sDate, sTime);
	}
	else
	{
		// Determine whether to use the normal, "to-user", or "from-user" format string.
		// The differences are the colors.  Then, output the message information line.
		var toNameUpper = pMsgHeader.to.toUpperCase();
		var msgToUser = ((toNameUpper == user.alias.toUpperCase()) || (toNameUpper == user.name.toUpperCase()) || (toNameUpper == user.handle.toUpperCase()));
		var fromNameUpper = pMsgHeader.from.toUpperCase();
		var msgIsFromUser = ((fromNameUpper == user.alias.toUpperCase()) || (fromNameUpper == user.name.toUpperCase()) || (fromNameUpper == user.handle.toUpperCase()));
		printf((msgToUser ? this.sMsgInfoToUserFormatStr :
		        (msgIsFromUser ? this.sMsgInfoFromUserFormatStr :
		        this.sMsgInfoFormatStr)),
			   msgNum,
			   (msgDeleted ? "\1r\1i*\1n" : " "),
			   pMsgHeader.from.substr(0, this.FROM_LEN),
			   pMsgHeader.to.substr(0, this.TO_LEN),
			   pMsgHeader.subject.substr(0, this.SUBJ_LEN),
			   sDate, sTime);
	}
	console.cleartoeol("\1"); // To clear away any extra text that may have been entered by the user
}
// For the traditional interface of DigDistMsgListerClass: Prompts the user to
// continue or read a message (by number).
//
// Parameters:
//  pStart: Whether or not we're on the first page (true or false)
//  pEnd: Whether or not we're at the last page (true or false)
//  pReturnOnMsgSelect: Optional - A boolean to specify whether or not
//                      to return when a message is selected to read.
//  pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
//                     changing to another sub-board.  Defaults to true.
//
// Return value: An object with the following properties:
//               continueOn: Boolean, whether or not the user wants to continue
//                           listing the messages
//               userInput: The user's input
//               selectedMsgOffset: The offset of the message selected to read,
//                                  if one was selected.  If a message was not
//                                  selected, this will be -1.
function DigDistMsgReader_PromptContinueOrReadMsg(pStart, pEnd, pReturnOnMsgSelect, pAllowChgSubBoard)
	// Create the return object and set some initial default values
	var retObj = new Object();
	retObj.continueOn = true;
	retObj.userInput = "";
	retObj.selectedMsgOffset = -1;

	var allowChgSubBoard = (typeof(pAllowChgSubBoard) == "boolean" ? pAllowChgSubBoard : true);

	var continueOn = true;
	// Prompt the user whether or not to continue or to read a message
	// (by message number).  Make use of the different prompt texts,
	// depending whether we're at the beginning, in the middle, or at
	// the end of the message list.
	var userInput = "";
	var allowedKeys = "?G"; // ? = help, G = Go to message #
	if (allowChgSubBoard)
		allowedKeys += "C"; // Change to another message area
	if (this.CanDelete() || this.CanDeleteLastMsg())
		allowedKeys += "D"; // Delete
	if (this.CanEdit())
		allowedKeys += "E"; // Edit
	if (pStart && pEnd)
	{
		// This is the only page.
		console.print(this.msgListOnlyOnePageContinuePrompt);
		// Get input from the user.  Allow only Q (quit).
		allowedKeys += "Q";
	}
	else if (pStart)
	{
		// We're on the first page.
		console.print(this.sStartContinuePrompt);
		// Get input from the user.  Allow only L (last), N (next), or Q (quit).
		allowedKeys += "LNQ";
	}
	else if (pEnd)
	{
		// We're on the last page.
		console.print(this.sEndContinuePrompt);
		// Get input from the user.  Allow only F (first), P (previous), or Q (quit).
		allowedKeys += "FPQ";
	}
	else
	{
		// We're neither on the first nor last page.  Allow F (first), L (last),
		// N (next), P (previous), or Q (quit).
		console.print(this.sContinuePrompt);
		allowedKeys += "FLNPQ";
	}
	// Get the user's input.  Allow the keys in allowedKeys or a number from 1
	// to the highest message number.
	userInput = console.getkeys(allowedKeys, this.HighestMessageNum()).toString();
	if (userInput == "Q")
		continueOn = false;

	// If the user has typed all numbers, then read that message.
	if ((userInput != "") && /^[0-9]+$/.test(userInput))
	{
		// If the user entered a valid message number, then let the user read the message.
		// The message number might be invalid if there are search results that
		// have non-continuous message numbers.
		if (this.IsValidMessageNum(userInput))
		{
			// Confirm with the user whether to read the message
			var readMsg = true;
			if (this.promptToReadMessage)
			{
				var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
				                        + "Read message "
				                        + this.colors["readMsgConfirmNumberColor"]
				                        + userInput + this.colors["readMsgConfirmColor"]
				                        + ": Are you sure";
				readMsg = console.yesno(sReadMsgConfirmText);
			}
			if (readMsg)
			{
				// Update the message list screen variables
				this.CalcMsgListScreenIdxVarsFromMsgNum(+userInput);
				// Let the user read the message
				if (pReturnOnMsgSelect)
				{
					// Fill a return object with the required values, and return it.
					retObj.continueOn = continueOn;
					retObj.userInput = userInput;
					retObj.selectedMsgOffset = userInput-1;
					return retObj;
				}
				else
				{
					this.readAMessage = true;
					this.ReadMessage(userInput-1);
				}
			}
			else
				this.deniedReadingMessage = true;

			// Prompt the user whether or not to continue listing
			// messages.
			if (this.promptToContinueListingMessages)
			{
				continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +
			}
		}
		else
		{
			// The user entered an invalid message number.
			console.print("\1n\1h\1w" + userInput + " \1y is not a valid message number.\1n");
			console.crlf();
			console.pause();
			continueOn = true;
		}
	}

	// Make sure color highlighting is turned off
	console.print("\1n");

	// Fill the return object with the required values, and return it.
	retObj.continueOn = continueOn;
	retObj.userInput = userInput;
	return retObj;
}
// For the DigDistMsgReader Class: Given a message number of a message in the
// current message area, shows the message to the user and allows the user to
// respond.
//
// Parameters:
//  pOffset: The offset of the message to be read
//
// Return value: And object with the following properties:
//               offsetValid: Boolean - Whether or not the passed-in offset was valid
//               userReplied: Boolean - Whether or not the user replied to the message.
//               msgbaseReOpened: Boolean - Whether or not the messagebase is open after
//                                the user replied to the message.  Will be true if
//                                the user didn't reply to the message.
function DigDistMsgReader_ReadMessage(pOffset)
{
	var retObj = new Object();
	retObj.offsetValid = true;
	retObj.userReplied = false;

	// Get the message header
	var msgHeader = this.GetMsgHdrByMsgNum(pOffset+1);
	if (msgHeader == null)
	{
		console.print("\1n" + this.text.invalidMsgNumText.replace("%d", +(pOffset+1)) + "\1n");
		console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
		retObj.offsetValid = false;
		return retObj;
	}

	// Show the message header.
	this.DisplaySyncMsgHeader(msgHeader);

	// Show the message body.  Make sure the text is word-wrapped
	// so that it looks good.
	var msgText = this.msgbase.get_msg_body(true, msgHeader.offset);
	var msgTextWrapped = word_wrap(msgText, console.screen_columns-1);
	console.print("\1n" + this.colors["msgBodyColor"]);
	console.putmsg(msgTextWrapped, P_NOATCODES);

	// Hack: If the "from" name in the header is blank (as it might be sometimes), then
	// set it to "All".  This prevents Synchronet from crashing, and it will also default
	// the "to" name in the user's reply to "All".
	if (msgHeader["from"] == "")
		msgHeader["from"] = "All";

	// Mark the message as read, if it was written to the current
	// user.
	var msgToUpper = msgHeader["to"].toUpperCase();
	if ((msgToUpper == user.alias.toUpperCase()) || (msgToUpper == user.name.toUpperCase()))
	{
		msgHeader.attr = (msgHeader.attr | MSG_READ);
		var wroteHeader = this.msgbase.put_msg_header(true, msgHeader.offset, msgHeader);
	}

	// If not reading personal email, then update the scan & last read message pointers.
	if (this.subBoardCode != "mail") // && !this.SearchTypePopulatesSearchResults()
	{
		if (msgHeader.number > msg_area.sub[this.subBoardCode].scan_ptr)
			msg_area.sub[this.subBoardCode].scan_ptr = msgHeader.number;
		msg_area.sub[this.subBoardCode].last_read = msgHeader.number;
	}

	// Allow the user to reply to the message, either publicly or privately.
	console.print("\1n\1cEnd of message. \1hR\1b)\1n\1ceply\1h\1b, " +
	              "\1cP\1b)\1n\1crivate reply\1h\1b, \1cENTER\1b/\1cN\1b)\1n\1co reply\1h\1g: \1n\1c");
	var userKey = console.getkeys("RPN").toString();
	var privateReply = (userKey == "P");
	if ((userKey == "R") || privateReply)
	{
		var replyRetObj = this.ReplyToMsg(msgHeader, msgText, privateReply, pOffset);
		retObj.userReplied = replyRetObj.postSucceeded;
		retObj.msgbaseReOpened = replyRetObj.msgbaseReOpened;
	}

	return retObj;
}
// For the DigDistMsgReader Class: Given a message number of a message in the
// current message area, shows the message to the user and allows the user to
// respond.  This is an enhanced version that allows scrolling up & down the
// message with the up & down arrow keys, and the left & right arrow keys will
// return from the function to allow calling code to navigate back & forth
// through the message sub-board.
//
// Parameters:
//  pOffset: The offset of the message to be read
//  pAllowChgArea: Optional boolean - Whether or not to allow changing the
//                 message area
//
// Return value: And object with the following properties:
//               offsetValid: Boolean - Whether or not the passed-in offset was valid
//               msgDeleted: Boolean - Whether or not the message is marked as deleted
//                           (not deleted by the user in the reader)
//               userReplied: Boolean - Whether or not the user replied to the message.
//               lastKeypress: The last keypress from the user - For navigation purposes
//               newMsgOffset: The offset of another message to read, if the user
//                             input another message number.  If the user did not
//                             input another message number, this will be -1.
//               nextAction: The next action for the caller to take.  This will be
//                           one of the values specified by the ACTION_* constants.
//                           This defaults to ACTION_NONE on error.
//               refreshEnhancedRdrHelpLine: Boolean - Whether or not to refresh the
//                                           enhanced reader help line on the screen
//                                           (for instance, if switched to the traditional
//                                            non-scrolling interface to read the message)
function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea)
{
	var retObj = new Object();
	retObj.offsetValid = true;
	retObj.msgDeleted = false;
	retObj.userReplied = false;
	retObj.lastKeypress = "";
	retObj.newMsgOffset = -1;
	retObj.nextAction = ACTION_NONE;
	retObj.refreshEnhancedRdrHelpLine = false;