Skip to content
Snippets Groups Projects
DDMsgReader.js 892 KiB
Newer Older
				{
					if ((msgHeader.auxattr & POLL_CLOSED) == 0)
					{
						// Only let the user close the poll if they created it
						if (userHandleAliasNameMatch(msgHeader.from))
						{
							// Prompt to confirm whether the user wants to close the poll
							if (!console.noyes("Close poll"))
							{
								// Close the poll (open the sub-board first)
								var msgbase = new MsgBase(this.subBoardCode);
								if (msgbase.open())
									if (closePollWithOpenMsgbase(msgbase, msgHeader.number))
									{
										msgHeader.auxattr |= POLL_CLOSED;
										pollCloseMsg = "\x01n\x01cThis poll was successfully closed.";
										pollCloseMsg = "\x01n\x01r\x01h* Failed to close this poll!";
									pollCloseMsg = "\x01n\x01r\x01h* Failed to open the sub-board!";
							pollCloseMsg = "\x01n\x01y\x01hCan't close this poll because it's not yours";
						pollCloseMsg = "\x01n\x01y\x01hThis poll is already closed";
				}
				else
					pollCloseMsg = "This message is not a poll";

				// Display the poll closing status message
				if (strip_ctrl(pollCloseMsg).length > 0)
				{
					console.print("\x01n" + pollCloseMsg + "\x01n");
					console.crlf();
					console.pause();
				}
				writeMessage = true;
				break;
			case this.enhReaderKeys.validateMsg: // Validate the message
				if (user.is_sysop && (this.subBoardCode != "mail") && msg_area.sub[this.subBoardCode].is_moderated)
				{
					var message = "";
					if (this.ValidateMsg(this.subBoardCode, msgHeader.number))
					{
						message = "\x01n\x01cMessage validation successful";
						// Refresh the message header in the arrays
						this.RefreshMsgHdrInArrays(msgHeader.number);
						// Exit out of the reader and come back to read
						// the same message again so that the voting results
						// are re-loaded and displayed on the screen.
						retObj.newMsgOffset = pOffset;
						retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
						continueOn = false;
					}
					else
					{
						message = "\x01n\x01y\x01hMessage validation failed!";
						writeMessage = true;
					}
					console.crlf();
					console.print(message);
					console.crlf();
					console.pause();
				}
				else
					writeMessage = false;
				break;
			case this.enhReaderKeys.quickValUser: // Quick-validate the user
				if (user.is_sysop)
					quickValidateLocalUser(msgHeader.from, this.scrollingReaderInterface && console.term_supports(USER_ANSI), this.quickUserValSetIndex);
				else
					writeMessage = false;
				break;
			case this.enhReaderKeys.bypassSubBoardInNewScan:
				// TODO: Finish
				writeMessage = false;
				/*
				if (this.doingMsgScan)
				{
					console.crlf();
					if (!console.noyes("Bypass this sub-board in newscans"))
					{
						continueOn = false;
						msg_area.sub[this.subBoardCode].scan_cfg &= SCAN_CFG_NEW;
					}
					else
						writeMessage = true;
				}
				else
					writeMessage = false;
				*/
				break;
			case this.enhReaderKeys.userSettings:
				var userSettingsRetObj = this.DoUserSettings_Traditional();
				// In case the user changed their twitlist, re-filter the messages for this sub-board
				if (userSettingsRetObj.userTwitListChanged)
				{
					console.crlf();
					console.print("\x01nTwitlist changed; re-filtering..");
					var tmpMsgbase = new MsgBase(this.subBoardCode);
					if (tmpMsgbase.open())
					{
						continueOn = false;
						writeMessage = false;
						var tmpAllMsgHdrs = tmpMsgbase.get_all_msg_headers(true);
						tmpMsgbase.close();
						this.FilterMsgHdrsIntoHdrsForCurrentSubBoard(tmpAllMsgHdrs, true);
						// If the user is currently reading a message a message by someone who is now
						// in their twit list, change the message currently being viewed.
						if (this.MsgHdrFromOrToInUserTwitlist(msgHeader))
						{
							var newReadableMsgOffset = this.FindNextReadableMsgIdx(pOffset, true);
							if (newReadableMsgOffset > -1)
							{
								retObj.newMsgOffset = newReadableMsgOffset;
								retObj.offsetValid = true;
								retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
							}
							else
								retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
						}
						else
						{
							// If there are still messages in this sub-board, and the message offset is beyond the last
							// message, then show the last message in the sub-board.  Otherwise, go to the next message area.
							if (this.hdrsForCurrentSubBoard.length > 0)
							{
								if (pOffset > this.hdrsForCurrentSubBoard.length)
								{
									//this.hdrsForCurrentSubBoard[this.hdrsForCurrentSubBoard.length-1].number
									retObj.newMsgOffset = this.hdrsForCurrentSubBoard.length - 1;
									retObj.offsetValid = true;
									retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
								}
							}
							else
								retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
						}
					}
					else
						console.print("\x01y\x01hFailed to open the messagbase!\x01\r\n\x01p");
					this.SetUpLightbarMsgListVars();
					writeMessage = true;
				}
				break;
			case this.enhReaderKeys.quit: // Quit
			case KEY_ESC:
				// Normally, if quitFromReaderGoesToMsgList is enabled, then do that, except
				// in indexed mode, allow going back to the indexed mode menu.
				if (this.userSettings.quitFromReaderGoesToMsgList && !this.indexedMode)
				{
					console.attributes = "N";
					console.crlf();
					console.print("Loading...");
					retObj.nextAction = ACTION_DISPLAY_MSG_LIST;
				}
				else
					retObj.nextAction = ACTION_QUIT;
				continueOn = false;
				break;
			default:
				// No need to do anything
				writeMessage = false;
				writePromptText = false;
				break;
		}
	}

	return retObj;
}

// For the ReadMessageEnhanced methods: This function converts a thread navigation
// key character to its corresponding thread type value
function keypressToThreadType(pKeypress, pEnhReaderKeys)
{
	var threadType = THREAD_BY_ID;
	switch (pKeypress)
	{
		case pEnhReaderKeys.prevMsgByTitle:
		case pEnhReaderKeys.nextMsgByTitle:
			threadType = THREAD_BY_TITLE;
			break;
		case pEnhReaderKeys.prevMsgByAuthor:
		case pEnhReaderKeys.nextMsgByAuthor:
			threadType = THREAD_BY_AUTHOR;
			break;
		case pEnhReaderKeys.prevMsgByToUser:
		case pEnhReaderKeys.nextMsgByToUser:
			threadType = THREAD_BY_TO_USER;
			break;
		case pEnhReaderKeys.prevMsgByThreadID:
		case pEnhReaderKeys.nextMsgByThreadID:
		default:
			threadType = THREAD_BY_ID;
			break;
// For the DigDistMsgReader class: For the enhanced reader method - Prepares the
// last 2 lines on the screen for propmting the user for something.
//
// Return value: An object containing x and y values representing the cursor
//               position, ready to prompt the user.
function DigDistMsgReader_EnhReaderPrepLast2LinesForPrompt()
{
	var promptPos = { x: this.msgAreaLeft, y: this.msgAreaBottom };
	// Write a line of characters above where the prompt will be placed,
	// to help get the user's attention.
	console.gotoxy(promptPos.x, promptPos.y-1);
	console.print("\x01n" + this.colors.enhReaderPromptSepLineColor);
	for (var lineCounter = 0; lineCounter < this.msgAreaWidth; ++lineCounter)
		console.print(HORIZONTAL_SINGLE);
	// Clear the inside of the message area, so as not to overwrite
	// the scrollbar character
	console.gotoxy(promptPos);
	for (var lineCounter = 0; lineCounter < this.msgAreaWidth; ++lineCounter)
		console.print(" ");
	// Position the cursor at the prompt location
	console.gotoxy(promptPos);
	
	return promptPos;
}

// For the DigDistMsgReader class: For the enhanced reader method - Looks for a
// later method that isn't marked for deletion.  If none is found, looks for a
// prior message that isn't marked for deletion.
//
// Parameters:
//  pOffset: The offset of the message to start at
//
// Return value: An object with the following properties:
//               newMsgOffset: The offset of the next readable message
//               nextAction: The next action (code) for the enhanced reader
//               continueInputLoop: Boolean - Whether or not to continue the input loop
//               promptGoToNextArea: Boolean - Whether or not to prompt the user to go
//                                   to the next message area
function DigDistMsgReader_LookForNextOrPriorNonDeletedMsg(pOffset)
{
	var retObj = {
		newMsgOffset: 0,
		nextAction: ACTION_NONE,
		continueInputLoop: true,
		promptGoToNextArea: false
	};

	// Look for a later message that isn't marked for deletion.
	// If none is found, then look for a prior message that isn't
	// marked for deletion.
	retObj.newMsgOffset = this.FindNextReadableMsgIdx(pOffset, true);
	if (retObj.newMsgOffset > -1)
	{
		retObj.continueInputLoop = false;
		retObj.nextAction = ACTION_GO_NEXT_MSG;
	}
	else
	{
		// No later message found, so look for a prior message.
		retObj.newMsgOffset = this.FindNextReadableMsgIdx(pOffset, false);
		if (retObj.newMsgOffset > -1)
		{
			retObj.continueInputLoop = false;
			retObj.nextAction = ACTION_GO_PREVIOUS_MSG;
		}
		else
		{
			// No prior message found.  We'll want to return from the enhanced
			// reader function (with message index -1) so that this script can
			// go onto the next message sub-board/group.  Also, set the next
			// action such that the calling method will go on to the next
			// message/sub-board.
			if (!curMsgSubBoardIsLast())
			{
				if (this.readingPersonalEmail)
				{
					retObj.continueInputLoop = false;
					retObj.nextAction = ACTION_QUIT;
				}
				else
					retObj.promptGoToNextArea =  true;
			}
			else
			{
				// We're not at the end of the sub-board or the current sub-board
				// is the last, so go ahead and exit.
				retObj.continueInputLoop = false;
				retObj.nextAction = ACTION_GO_NEXT_MSG;
			}
		}
	}
	return retObj;
}

// For the DigDistMsgReader class: Writes the help line for enhanced reader
// mode.
//
// Parameters:
//  pScreenRow: Optional - The screen row to write the help line on.  If not
//              specified, the last row on the screen will be used.
//  pDisplayChgAreaOpt: Optional boolean - Whether or not to show the "change area" option.
//                      Defaults to true.
function DigDistMsgReader_DisplayEnhancedMsgReadHelpLine(pScreenRow, pDisplayChgAreaOpt)
{
	var displayChgAreaOpt = (typeof(pDisplayChgAreaOpt) == "boolean" ? pDisplayChgAreaOpt : true);
	// Move the cursor to the desired location on the screen and display the help line
	console.gotoxy(1, typeof(pScreenRow) == "number" ? pScreenRow : console.screen_rows);
	// TODO: Mouse: console.print replaced with console.putmsg for mouse click hotspots
	//console.print(displayChgAreaOpt ? this.enhReadHelpLine : this.enhReadHelpLineWithoutChgArea);
	// console.putmsg() handles @-codes, which we use for mouse click tracking
	console.putmsg(displayChgAreaOpt ? this.enhReadHelpLine : this.enhReadHelpLineWithoutChgArea);
}

// For the DigDistMsgReader class: Goes back to the prior readable sub-board
// (accounting for search results, etc.).  Changes the object's subBoardCode,
// msgbase object, etc.
//
// Parameters:
//  pAllowChgMsgArea: Boolean - Whether or not the user is allowed to change
//                    to another message area
//  pPromptPrevIfNoResults: Optional boolean - Whether or not to prompt the user to
//                          go to the previous area if there are no search results.
//
// Return value: An object with the following properties:
//               changedMsgArea: Boolean - Whether or not this method successfully
//                               changed to a prior message area
//               msgIndex: The message index for the new sub-board.  Will be -1
//                         if there is no new sub-board or otherwise invalid
//                         scenario.
//               shouldStopReading: Whether or not the script should stop letting
//                                  the user read messages
function DigDistMsgReader_GoToPrevSubBoardForEnhReader(pAllowChgMsgArea, pPromptPrevIfNoResults)
	var retObj = {
		changedMsgArea: false,
		msgIndex: -1,
		shouldStopReading: false
	};

	// Only allow this if pAllowChgMsgArea is true and we're not reading personal
	// email.  If we're reading personal email, then msg_area.sub is unavailable
	// for the "mail" internal code.
	if (pAllowChgMsgArea && (this.subBoardCode != "mail"))
	{
		// continueGoingToPrevSubBoard specifies whether or not to continue
		// going to the previous sub-boards in case there is search text
		// specified.
		var continueGoingToPrevSubBoard = true;
		while (continueGoingToPrevSubBoard)
		{
			// Allow going to the previous message sub-board/group.
			var msgGrpIdx = msg_area.sub[this.subBoardCode].grp_index;
			var subBoardIdx = msg_area.sub[this.subBoardCode].index;
			var readMsgRetObj = findNextOrPrevNonEmptySubBoard(msgGrpIdx, subBoardIdx, false);
			// If a different sub-board was found, then go to that sub-board.
			if (readMsgRetObj.foundSubBoard && readMsgRetObj.subChanged)
			{
				bbs.cursub = 0;
				bbs.curgrp = readMsgRetObj.grpIdx;
				bbs.cursub = readMsgRetObj.subIdx;
				this.setSubBoardCode(readMsgRetObj.subCode);
				if (this.searchType == SEARCH_NONE || !this.SearchingAndResultObjsDefinedForCurSub())
					continueGoingToPrevSubBoard = false; // No search results, so don't keep going to the previous sub-board.
					// Go to the user's last read message.  If the message index ends up
					// below 0, then go to the last message not marked as deleted.
					// We probably shouldn't use GetMsgIdx() yet because the arrays of
					// message headers have not been populated for the next area yet
					retObj.msgIndex = this.AbsMsgNumToIdx(msg_area.sub[this.subBoardCode].last_read);
					//retObj.msgIndex = this.GetMsgIdx(msg_area.sub[this.subBoardCode].last_read);
					if (retObj.msgIndex >= 0)
						retObj.changedMsgArea = true;
					else
						// Look for the last message not marked as deleted
						var readableMsgIdx = this.FindNextReadableMsgIdx(this.NumMessages(), false);
						// If a non-deleted message was found, then set retObj.msgIndex to it.
						// Otherwise, tell the user there are no messages in this sub-board
						// and return.
						if (readableMsgIdx > -1)
							retObj.msgIndex = readableMsgIdx;
							retObj.msgIndex = this.NumMessages() - 1; // Shouldn't get here
						var newLastRead = this.IdxToAbsMsgNum(retObj.msgIndex);
						if (newLastRead > -1)
							msg_area.sub[this.subBoardCode].last_read = newLastRead;
				}
				// Set the hotkey help line again, as this sub-board might have
				// different settings for whether messages can be edited or deleted,
				// then refresh it on the screen.
				var oldHotkeyHelpLine = this.enhReadHelpLine;
				this.SetEnhancedReaderHelpLine();
				// If a search is is specified that would populate the search
				// results, then populate this.msgSearchHdrs for the current
				// sub-board if there is search text specified.  If there
				// are no search results, then ask the user if they want
				// to continue searching the message areas.
				if (this.SearchTypePopulatesSearchResults())
				{
					if (this.PopulateHdrsIfSearch_DispErrorIfNoMsgs(false, true, false))
						continueGoingToPrevSubBoard = false;
						retObj.msgIndex = this.NumMessages() - 1;
						if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
							this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
					}
					else // No search results in this sub-board
					{
						var promptPrevIfNoResults = (typeof(pPromptPrevIfNoResults) === "boolean" ? pPromptPrevIfNoResults : true);
						if (promptPrevIfNoResults)
							continueGoingToPrevSubBoard = !console.noyes("Continue searching");
						if (!continueGoingToPrevSubBoard)
						{
							retObj.shouldStopReading = true;
							return retObj;
						}
					}
					retObj.changedMsgArea = true;
					this.PopulateHdrsForCurrentSubBoard();
					if ((oldHotkeyHelpLine != this.enhReadHelpLine) && this.scrollingReaderInterface && console.term_supports(USER_ANSI))
						this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
				}
			}
			else
			{
				// Didn't find a prior sub-board with readable messages.
				// We could stop and exit the script here by doing the following,
				// but I'd rather let the user exit when they want to.

				continueGoingToPrevSubBoard = false;
				// Show a message telling the user that there are no prior
				// messages or sub-boards.  Then, refresh the hotkey help line.
				writeWithPause(this.msgAreaLeft, console.screen_rows,
									"\x01n\x01h\x01y* No prior messages or no message in prior message areas.",
									ERROR_PAUSE_WAIT_MS, "\x01n", true);
				if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
					this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
			}
		}
	}

	return retObj;
}

// For the DigDistMsgReader class: Goes to the next readable sub-board
// (accounting for search results, etc.).  Changes the object's subBoardCode,
// msgbase object, etc.
//
// Parameters:
//  pAllowChgMsgArea: Boolean - Whether or not the user is allowed to change
//                    to another message area
//  pPromptNextIfNoResults: Optional boolean - Whether or not to prompt the user to
//                          go to the next area if there are no search results.
//                          Defaults to true.
//
// Return value: An object with the following properties:
//               changedMsgArea: Boolean - Whether or not this method successfully
//                               changed to a prior message area
//               msgIndex: The message index for the new sub-board.  Will be -1
//                         if there is no new sub-board or otherwise invalid
//                         scenario.
//               shouldStopReading: Whether or not the script should stop letting
//                                  the user read messages
function DigDistMsgReader_GoToNextSubBoardForEnhReader(pAllowChgMsgArea, pPromptNextIfNoResults)
	var retObj = {
		changedMsgArea: false,
		msgIndex: -1,
		shouldStopReading: false
	};

	// Only allow this if pAllowChgMsgArea is true and we're not reading personal
	// email.  If we're reading personal email, then msg_area.sub is unavailable
	// for the "mail" internal code.
	if (pAllowChgMsgArea && (this.subBoardCode != "mail"))
	{
		// continueGoingToNextSubBoard specifies whether or not to continue
		// advancing to the next sub-boards in case there is search text
		// specified.
		var continueGoingToNextSubBoard = true;
		while (continueGoingToNextSubBoard)
		{
			// Allow going to the next message sub-board/group.
			var msgGrpIdx = msg_area.sub[this.subBoardCode].grp_index;
			var subBoardIdx = msg_area.sub[this.subBoardCode].index;
			var readMsgRetObj = findNextOrPrevNonEmptySubBoard(msgGrpIdx, subBoardIdx, true);
			// If a different sub-board was found, then go to that sub-board.
			if (readMsgRetObj.foundSubBoard && readMsgRetObj.subChanged)
			{
				retObj.msgIndex = 0;
				bbs.cursub = 0;
				bbs.curgrp = readMsgRetObj.grpIdx;
				bbs.cursub = readMsgRetObj.subIdx;
				this.setSubBoardCode(readMsgRetObj.subCode);
				if ((this.searchType == SEARCH_NONE) || !this.SearchingAndResultObjsDefinedForCurSub())
					continueGoingToNextSubBoard = false; // No search results, so don't keep going to the next sub-board.
					// Go to the user's last read message.  If the message index ends up
					// below 0, then go to the first message not marked as deleted.
					retObj.msgIndex = this.AbsMsgNumToIdx(msg_area.sub[this.subBoardCode].last_read);
					// We probably shouldn't use GetMsgIdx() yet because the arrays of
					// message headers have not been populated for the next area yet
					//retObj.msgIndex = this.GetMsgIdx(msg_area.sub[this.subBoardCode].last_read);
					if (retObj.msgIndex >= 0)
						retObj.changedMsgArea = true;
					else
						// Set the index of the message to display - Look for the
						// first message not marked as deleted
						var readableMsgIdx = this.FindNextReadableMsgIdx(this.NumMessages()-1, true);
						// If a non-deleted message was found, then set retObj.msgIndex to it.
						// Otherwise, tell the user there are no messages in this sub-board
						// and return.
							var newLastRead = this.IdxToAbsMsgNum(readableMsgIdx);
							if (newLastRead > -1)
								msg_area.sub[this.subBoardCode].last_read = newLastRead;
				}
				// Set the hotkey help line again, as this sub-board might have
				// different settings for whether messages can be edited or deleted,
				// then refresh it on the screen.
				var oldHotkeyHelpLine = this.enhReadHelpLine;
				this.SetEnhancedReaderHelpLine();
				// If a search is is specified that would populate the search
				// results, then populate this.msgSearchHdrs for the current
				// sub-board if there is search text specified.  If there
				// are no search results, then ask the user if they want
				// to continue searching the message areas.
				if (this.SearchTypePopulatesSearchResults())
				{
					if (this.PopulateHdrsIfSearch_DispErrorIfNoMsgs(false, true, false))
						retObj.changedMsgArea = true;
						continueGoingToNextSubBoard = false;
						this.PopulateHdrsForCurrentSubBoard();
						retObj.msgIndex = 0;
						if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
							this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
					}
					else // No search results in this sub-board
					{
						var promptNextIfNoresults = (typeof(pPromptNextIfNoResults) === "boolean" ? pPromptNextIfNoResults : true);
						if (promptNextIfNoresults)
							continueGoingToNextSubBoard = !console.noyes("Continue searching");
						if (!continueGoingToNextSubBoard)
						{
							retObj.shouldStopReading = true;
							return retObj;
						}
					// There is no search.  Populate the arrays of all headers
					// for this sub-board
					this.PopulateHdrsForCurrentSubBoard();
					retObj.msgIndex = this.GetMsgIdx(msg_area.sub[this.subBoardCode].last_read);
					if (retObj.msgIndex == -1)
						retObj.msgIndex = 0;
				}
			}
			else
			{
				// Didn't find later sub-board with readable messages.
				// We could stop and exit the script here by doing the following,
				// but I'd rather let the user exit when they want to.
				//retObj.shouldStopReading = true;
				//return retObj;

				continueGoingToNextSubBoard = false;
				// Show a message telling the user that there are no more
				// messages or sub-boards.  Then, refresh the hotkey help line.
				writeWithPause(this.msgAreaLeft, console.screen_rows,
				               "\x01n\x01h\x01y* No more messages or message areas.",
				               ERROR_PAUSE_WAIT_MS, "\x01n", true);
				if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
					this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
			}
		}
	}

	return retObj;
}

// For the DigDistMsgReader Class: Prepares the variables that keep track of the
// traditional-interface message list position, current messsage number, etc.
function DigDistMsgReader_SetUpTraditionalMsgListVars()
{
	// If a search is specified, then just start at the first message.
	// If no search is specified, then get the index of the user's last read
	// message.  Then, figure out which page it's on and set the lightbar list
	// index & cursor position variables accordingly.
	var lastReadMsgIdx = 0;
	if (!this.SearchingAndResultObjsDefinedForCurSub())
	{
		lastReadMsgIdx = this.GetLastReadMsgIdxAndNum().lastReadMsgIdx;
		if (lastReadMsgIdx == -1)
			lastReadMsgIdx = 0;
	}
	var pageNum = findPageNumOfItemNum(lastReadMsgIdx+1, this.tradMsgListNumLines, this.NumMessages(),
									   this.userSettings.listMessagesInReverse);
	this.CalcTraditionalMsgListTopIdx(pageNum);
	if (!this.userSettings.listMessagesInReverse && (this.tradListTopMsgIdx > lastReadMsgIdx))
		this.tradListTopMsgIdx -= this.tradMsgListNumLines;
}

// For the DigDistMsgReader Class: Prepares the variables that keep track of the
// lightbar message list position, current messsage number, etc.
function DigDistMsgReader_SetUpLightbarMsgListVars()
{
	// If no search is specified or if reading personal email, then get the index
	// of the user's last read message.  Then, figure out which page it's on and
	// set the lightbar list index & cursor position variables accordingly.
	if (!this.SearchingAndResultObjsDefinedForCurSub() || this.readingPersonalEmail)
		lastReadMsgIdx = this.GetLastReadMsgIdxAndNum().lastReadMsgIdx;
	else
	{
		// A search was specified.  If reading personal email, then set the
		// message index to the last read message.
		if (this.readingPersonalEmail)
		{
			lastReadMsgIdx = this.GetLastReadMsgIdxAndNum().lastReadMsgIdx;
	var pageNum = findPageNumOfItemNum(lastReadMsgIdx+1, this.lightbarMsgListNumLines, this.NumMessages(),
	                                   this.userSettings.listMessagesInReverse);
	this.CalcLightbarMsgListTopIdx(pageNum);
	var initialCursorRow = 0;
	if (this.userSettings.listMessagesInReverse)
		initialCursorRow = this.lightbarMsgListStartScreenRow+(this.lightbarListTopMsgIdx-lastReadMsgIdx);
	else
	{
		if (this.lightbarListTopMsgIdx > lastReadMsgIdx)
			this.lightbarListTopMsgIdx -= this.lightbarMsgListNumLines;
		initialCursorRow = this.lightbarMsgListStartScreenRow+(lastReadMsgIdx-this.lightbarListTopMsgIdx);
	}
	if (this.userSettings.listMessagesInReverse)
	{
		this.lightbarListSelectedMsgIdx = this.NumMessages() - lastReadMsgIdx - 1;
		if (this.lightbarListSelectedMsgIdx < 0)
			this.lightbarListSelectedMsgIdx = 0;
	}
	else
		this.lightbarListSelectedMsgIdx = lastReadMsgIdx;
	this.lightbarListCurPos = { x: 1, y: initialCursorRow };
}

// For the DigDistMsgReader Class: Writes the message list column headers at the
// top of the screen.
function DigDistMsgReader_WriteMsgListScreenTopHeader()
{
	console.home();

	// If we will be displaying the message group and sub-board in the
	// header at the top of the screen (an additional 2 lines), then
	// update nMaxLines and nListStartLine to account for this.
	if (this.displayBoardInfoInHeader && canDoHighASCIIAndANSI()) // console.term_supports(USER_ANSI)
	{
		var curpos = console.getxy();
		// Figure out the message group name & sub-board name
		// For the message group name, we can also use msgbase.cfg.grp_name in
		var msgGroupName = "";
		if (this.subBoardCode == "mail")
			msgGroupName = "Mail";
		else
			msgGroupName = msg_area.grp_list[msgbase.cfg.grp_number].description;
		var subBoardName = "Unspecified";
		if (msgbase.open())
		{
			if (msgbase.cfg != null)
				subBoardName = msgbase.cfg.description;
			else if ((msgbase.subnum == -1) || (msgbase.subnum == 65535))
				subBoardName = "Electronic Mail";
			else
				subBoardName = "Unspecified";
			msgbase.close();
		}
		// Display the message group name
		console.print(this.colors["msgListHeaderMsgGroupTextColor"] + "Msg group: " +
		this.colors["msgListHeaderMsgGroupNameColor"] + msgGroupName);
		console.cleartoeol(); // Fill to the end of the line with the current colors
		// Display the sub-board name on the next line
		++curpos.y;
		console.gotoxy(curpos);
		console.print(this.colors.msgListHeaderSubBoardTextColor + "Sub-board: " +
		this.colors["msgListHeaderMsgSubBoardName"] + subBoardName);
		console.cleartoeol(); // Fill to the end of the line with the current colors
		++curpos.y;
		console.gotoxy(curpos);
	}

	// Write the message listing column headers
	if (this.showScoresInMsgList)
		printf(this.colors.msgListColHeader + this.sMsgListHdrFormatStr, "Msg#", "From", "To", "Subject", "+/-", "Date", "Time");
	else
		printf(this.colors.msgListColHeader + this.sMsgListHdrFormatStr, "Msg#", "From", "To", "Subject", "Date", "Time");
}
// For the DigDistMsgReader Class: Lists a screenful of message header information.
//
// Parameters:
//  pTopIndex: The index (offset) of the top message
//  pMaxLines: The maximum number of lines to output to the screen
//
// Return value: Boolean, whether or not the last message output to the
//               screen is the last message in the sub-board.
function DigDistMsgReader_ListScreenfulOfMessages(pTopIndex, pMaxLines)
{

	var curpos = console.getxy();
	var msgIndex = 0;
	if (this.userSettings.listMessagesInReverse)
		var endIndex = pTopIndex - pMaxLines + 1; // The index of the last message to display
		for (msgIndex = pTopIndex; (msgIndex >= 0) && (msgIndex >= endIndex); --msgIndex)
		{
			// The following line which sets console.line_counter to 0 is a
			// kludge to disable Synchronet's automatic pausing after a
			// screenful of text, so that this script can have more control
			// over screen pausing.
			console.line_counter = 0;
			// Get the message header (it will be a MsgHeader object) and
			// display it.
			msgHeader = this.GetMsgHdrByIdx(msgIndex, this.showScoresInMsgList);
			if (msgHeader == null)
				continue;

			// Display the message info
			this.PrintMessageInfo(msgHeader, false, msgIndex+1);
			if (console.term_supports(USER_ANSI))
			{
				++curpos.y;
				console.gotoxy(curpos);
			}
			else
				console.crlf();
		var endIndex = pTopIndex + pMaxLines; // One past the last message index to display
		for (msgIndex = pTopIndex; (msgIndex < this.NumMessages()) && (msgIndex < endIndex); ++msgIndex)
		{
			// The following line which sets console.line_counter to 0 is a
			// kludge to disable Synchronet's automatic pausing after a
			// screenful of text, so that this script can have more control
			// over screen pausing.
			console.line_counter = 0;
			// Get the message header (it will be a MsgHeader object) and
			// display it.
			var msgHeader = this.GetMsgHdrByIdx(msgIndex, this.showScoresInMsgList);
			if (msgHeader == null)
				continue;

			// Display the message info
			this.PrintMessageInfo(msgHeader, false, msgIndex+1);
			if (console.term_supports(USER_ANSI))
			{
				++curpos.y;
				console.gotoxy(curpos);
			}
			else
				console.crlf();
		atLastPage = (msgIndex == this.NumMessages());
	}
// For the DigDistMsgReader Class: Displays the help screen for the message list.
//  pChgSubBoardAllowed: Whether or not changing to another sub-board is allowed
//  pPauseAtEnd: Boolean, whether or not to pause at the end.
function DigDistMsgReader_DisplayMsgListHelp(pChgSubBoardAllowed, pPauseAtEnd)
{
	DisplayProgramInfo();

	// Display help specific to which interface is being used.
	if (this.msgListUseLightbarListInterface)
		this.DisplayLightbarMsgListHelp(false, pChgSubBoardAllowed);
		this.DisplayTraditionalMsgListHelp(false, pChgSubBoardAllowed);

	// If pPauseAtEnd is true, then output a newline and
	// prompt the user whether or not to continue.
// For the DigDistMsgReader Class: Displays help for the traditional-interface
// message list
//
// Parameters:
//  pDisplayHeader: Whether or not to display a help header at the beginning
//  pChgSubBoardAllowed: Whether or not changing to another sub-board is allowed
//  pPauseAtEnd: Boolean, whether or not to pause at the end.
function DigDistMsgReader_DisplayTraditionalMsgListHelp(pDisplayHeader, pChgSubBoardAllowed, pPauseAtEnd)
{
	// If pDisplayHeader is true, then display the program information.
	if (pDisplayHeader)
		DisplayProgramInfo();

	// Display information about the current sub-board and search results.
	console.print("\x01n\x01cCurrently reading \x01g" + subBoardGrpAndName(this.subBoardCode));
	console.crlf();
	// If the user isn't reading personal messages (i.e., is reading a sub-board),
	// then output the total number of messages in the sub-board.  We probably
	// shouldn't output the total number of messages in the "mail" area, because
	// that includes more than the current user's email.
	if (this.subBoardCode != "mail")
	{
		var numOfMessages = 0;
		var msgbase = new MsgBase(this.subBoardCode);
		if (msgbase.open())
		{
			numOfMessages = msgbase.total_msgs;
			msgbase.close();
		}
		console.print("\x01n\x01cThere are a total of \x01g" + numOfMessages + " \x01cmessages in the current area.");
		console.crlf();
	}
	// If there is currently a search (which also includes personal messages),
	// then output the number of search results/personal messages.
	if (this.SearchingAndResultObjsDefinedForCurSub())
	{
		var numSearchResults = this.NumMessages();
		var resultsWord = (numSearchResults > 1 ? "results" : "result");
		console.print("\x01n\x01c");
			console.print("You have \x01g" + numSearchResults + " \x01c" + (numSearchResults == 1 ? "message" : "messages") + ".");
				console.print("There is \x01g1 \x01csearch result.");
				console.print("There are \x01g" + numSearchResults + " \x01csearch results.");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor);
	displayTextWithLineBelow("Page navigation and message selection", false,
	                         this.colors.tradInterfaceHelpScreenColor, "\x01k\x01h");
	console.print(this.colors.tradInterfaceHelpScreenColor);
	console.print("The message lister will display a page of message header information.  At\r\n");
	console.print("the end of each page, a prompt is displayed, allowing you to navigate to\r\n");
	console.print("the next page, previous page, first page, or the last page.  If you would\r\n");
	console.print("like to read a message, you may type the message number, followed by\r\n");
	console.print("the enter key if the message number is short.  To quit the listing, press\r\n");
	console.print("the Q key.\r\n\r\n");
	this.DisplayMessageListNotesHelp();
	console.crlf();
	console.crlf();
	displayTextWithLineBelow("Summary of the keyboard commands:", false,
	                         this.colors.tradInterfaceHelpScreenColor, "\x01k\x01h");
	console.print(this.colors.tradInterfaceHelpScreenColor);
	console.print("\x01n\x01h\x01cN" + this.colors.tradInterfaceHelpScreenColor + ": Go to the next page\r\n");
	console.print("\x01n\x01h\x01cP" + this.colors.tradInterfaceHelpScreenColor + ": Go to the previous page\r\n");
	console.print("\x01n\x01h\x01cF" + this.colors.tradInterfaceHelpScreenColor + ": Go to the first page\r\n");
	console.print("\x01n\x01h\x01cL" + this.colors.tradInterfaceHelpScreenColor + ": Go to the last page\r\n");
	console.print("\x01n\x01h\x01cG" + this.colors.tradInterfaceHelpScreenColor + ": Go to a specific message by number (the message will appear at the top\r\n" +
	console.print("\x01n\x01h\x01cNumber" + this.colors.tradInterfaceHelpScreenColor + ": Read the message corresponding with that number\r\n");
	//console.print("The following commands are available only if you have permission to do so:\r\n");
	if (this.CanDelete() || this.CanDeleteLastMsg())
		console.print("\x01n\x01h\x01cD" + this.colors.tradInterfaceHelpScreenColor + ": Mark a message for deletion\r\n");
		console.print("\x01n\x01h\x01cE" + this.colors.tradInterfaceHelpScreenColor + ": Edit an existing message\r\n");
		console.print("\x01n\x01h\x01cC" + this.colors.tradInterfaceHelpScreenColor + ": Change to another message sub-board\r\n");
	console.print("\x01n\x01h\x01cS" + this.colors.tradInterfaceHelpScreenColor + ": Select messages (for batch delete, etc.)\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  A message number or multiple numbers can be entered separated by commas or\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  spaces.  Additionally, a range of numbers (separated by a dash) can be used.\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  Examples:\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  125\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  1,2,3\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  1 2 3\r\n");
	console.print("\x01n" + this.colors.tradInterfaceHelpScreenColor + "  1,2,10-20\r\n");
	console.print("\x01n\x01h\x01cCTRL-U" + this.colors.tradInterfaceHelpScreenColor + ": Change your user settings\r\n");
	console.print("\x01n\x01h\x01cCTRL-D" + this.colors.tradInterfaceHelpScreenColor + ": Batch delete selected messages\r\n");
	console.print("\x01n\x01h\x01cQ" + this.colors.tradInterfaceHelpScreenColor + ": Quit\r\n");
	if (this.indexedMode)
		console.print(" Currently in indexed mode; quitting will quit back to the index list.\r\n");
	console.print("\x01n\x01h\x01c?" + this.colors.tradInterfaceHelpScreenColor + ": Show this help screen\r\n\r\n");

	// If pPauseAtEnd is true, then output a newline and
	// prompt the user whether or not to continue.

	// Don't set this here - This is only ever called by DisplayMsgListHelp()
	//console.aborted = false;
// For the DigDistMsgReader Class: Displays help for the lightbar message list
//
// Parameters:
//  pDisplayHeader: Whether or not to display a help header at the beginning
//  pChgSubBoardAllowed: Whether or not changing to another sub-board is allowed
//  pPauseAtEnd: Boolean, whether or not to pause at the end.
function DigDistMsgReader_DisplayLightbarMsgListHelp(pDisplayHeader, pChgSubBoardAllowed, pPauseAtEnd)
{
	// If pDisplayHeader is true, then display the program information.
	if (pDisplayHeader)
		DisplayProgramInfo();

	// Display information about the current sub-board and search results.
	console.print("\x01n\x01cCurrently reading \x01g" + subBoardGrpAndName(this.subBoardCode));
	console.crlf();
	// If the user isn't reading personal messages (i.e., is reading a sub-board),
	// then output the total number of messages in the sub-board.  We probably
	// shouldn't output the total number of messages in the "mail" area, because
	// that includes more than the current user's email.
	if (this.subBoardCode != "mail")
	{
		var numOfMessages = 0;
		var msgbase = new MsgBase(this.subBoardCode);
		if (msgbase.open())
		{
			numOfMessages = msgbase.total_msgs;
			msgbase.close();
		}
		console.print("\x01n\x01cThere are a total of \x01g" + numOfMessages + " \x01cmessages in the current sub-board.");
		console.crlf();
	}
	// If there is currently a search (which also includes personal messages),
	// then output the number of search results/personal messages.
	if (this.SearchingAndResultObjsDefinedForCurSub())
	{
		var numSearchResults = this.NumMessages();
		var resultsWord = (numSearchResults > 1 ? "results" : "result");
		console.print("\x01n\x01c");
			console.print("You have \x01g" + numSearchResults + " \x01c" + (numSearchResults == 1 ? "message" : "messages") + ".");
				console.print("There is \x01g1 \x01csearch result.");
				console.print("There are \x01g" + numSearchResults + " \x01csearch results.");
		}
		console.crlf();
	}
	console.crlf();

	displayTextWithLineBelow("Lightbar interface: Page navigation and message selection",
	                         false, this.colors.tradInterfaceHelpScreenColor, "\x01k\x01h");
	console.print(this.colors.tradInterfaceHelpScreenColor);
	console.print("The message lister will display a page of message header information.  You\r\n");
	console.print("may use the up and down arrows to navigate the list of messages.  The\r\n");
	console.print("currently-selected message will be highlighted as you navigate through\r\n");
	console.print("the list.  To read a message, navigate to the desired message and press\r\n");
	console.print("the enter key.  You can also read a message by typing its message number.\r\n");
	console.print("To quit out of the message list, press the Q key.\r\n\r\n");
	this.DisplayMessageListNotesHelp();
	console.crlf();
	console.crlf();
	displayTextWithLineBelow("Summary of the keyboard commands:", false, this.colors.tradInterfaceHelpScreenColor, "\x01k\x01h");
	console.print(this.colors.tradInterfaceHelpScreenColor);
	console.print("\x01n\x01h\x01cDown arrow" + this.colors.tradInterfaceHelpScreenColor + ": Move the cursor down/select the next message\r\n");
	console.print("\x01n\x01h\x01cUp arrow" + this.colors.tradInterfaceHelpScreenColor + ": Move the cursor up/select the previous message\r\n");
	console.print("\x01n\x01h\x01cN" + this.colors.tradInterfaceHelpScreenColor + ": Go to the next page\r\n");