Skip to content
Snippets Groups Projects
DDMsgAreaChooser.js 133 KiB
Newer Older
//               subIdx: The sub-board index.    Will be -1 if the sub-board code is invalid.
function getGrpAndSubIdxesFromCode(pSubBoardCode, pDefaultToZero)
{
	var retVal = {
		grpIdx: (pDefaultToZero ? 0 : -1),
		subIdx: (pDefaultToZero ? 0 : -1)
	};
	if ((typeof(pSubBoardCode) == "string") && (pSubBoardCode != ""))
	{
		retVal.grpIdx = msg_area.sub[pSubBoardCode].grp_index;
		retVal.subIdx = msg_area.sub[pSubBoardCode].index;
	}
	return retVal;
}

// Inputs a string from the user, with a timeout
//
// Parameters:
//  pMode: The mode bits to use for the input (i.e., defined in sbbsdefs.js)
//  pMaxLength: The maximum length of the string (0 or less for no limit)
//  pTimeout: The timeout (in milliseconds).  When the timeout is reached,
//            input stops and the user's input is returned.
//
// Return value: The user's input (string)
function getStrWithTimeout(pMode, pMaxLength, pTimeout)
{
	var inputStr = "";

	var mode = K_NONE;
		timeout = pTimeout;

	var setNormalAttrAtEnd = false;
	if (((mode & K_LINE) == K_LINE) && (maxWidth > 0) && console.term_supports(USER_ANSI))
	{
		var curPos = console.getxy();
		printf("\1n\1w\1h\1" + "4%" + maxWidth + "s", "");
		console.gotoxy(curPos);
		setNormalAttrAtEnd = true;
	}

	var curPos = console.getxy();
	var userKey = "";
	do
	{
		userKey = console.inkey(mode, timeout);
		if ((userKey.length > 0) && isPrintableChar(userKey))
		{
			var allowAppendChar = true;
			if ((maxWidth > 0) && (inputStr.length >= maxWidth))
				allowAppendChar = false;
			if (allowAppendChar)
			{
				inputStr += userKey;
				console.print(userKey);
				++curPos.x;
			}
		}
		else if (userKey == BACKSPACE)
		{
			if (inputStr.length > 0)
			{
				inputStr = inputStr.substr(0, inputStr.length-1);
				console.gotoxy(curPos.x-1, curPos.y);
				console.print(" ");
				console.gotoxy(curPos.x-1, curPos.y);
				--curPos.x;
			}
		}
		else if (userKey == KEY_ENTER)
			userKey = "";
	} while(userKey.length > 0);

	if (setNormalAttrAtEnd)
		console.print("\1n");

	return inputStr;
}

// Returns whether or not a character is printable.
function isPrintableChar(pText)
{
   // Make sure pText is valid and is a string.
   if (typeof(pText) != "string")
      return false;
   if (pText.length == 0)
      return false;

   // Make sure the character is a printable ASCII character in the range of 32 to 254,
   // except for 127 (delete).
   var charCode = pText.charCodeAt(0);
   return ((charCode > 31) && (charCode < 255) && (charCode != 127));
}

// Finds the page number of an item, given some text to search for in the item.
//
// Parameters:
//  pText: The text to search for in the items
//  pNumItemsPerPage: The number of items per page
//  pSubBoard: Boolean - If true, search the sub-board list for the given group index.
//             If false, search the group list.
//  pStartItemIdx: The item index to start at
//  pGrpIdx: The index of the group to search in (only for doing a sub-board search)
//
// Return value: An object containing the following properties:
//               pageNum: The page number of the item (1-based; will be 0 if not found)
//               pageTopIdx: The index of the top item on the page (or -1 if not found)
//               itemIdx: The index of the item (or -1 if not found)
function getPageNumFromSearch(pText, pNumItemsPerPage, pSubBoard, pStartItemIdx, pGrpIdx)
{
	var retObj = {
		pageNum: 0,
		pageTopIdx: -1,
		itemIdx: -1
	};

	// Sanity checking
	if ((typeof(pText) != "string") || (typeof(pNumItemsPerPage) !== "number") || (typeof(pSubBoard) != "boolean"))
		return retObj;

	// Convert the text to uppercase for case-insensitive searching
	var srchText = pText.toUpperCase();
	if (pSubBoard)
	{
		if ((typeof(pGrpIdx) === "number") && (pGrpIdx >= 0) && (pGrpIdx < msg_area.grp_list.length))
		{
			// Go through the sub-board list of the given group and
			// search for text in the descriptions
			for (var i = pStartItemIdx; i < msg_area.grp_list[pGrpIdx].sub_list.length; ++i)
			{
				if ((msg_area.grp_list[pGrpIdx].sub_list[i].description.toUpperCase().indexOf(srchText) > -1) ||
				    (msg_area.grp_list[pGrpIdx].sub_list[i].name.toUpperCase().indexOf(srchText) > -1))
				{
					retObj.itemIdx = i;
					// Figure out the page number and top index for the page
					var pageObj = calcPageNumAndTopPageIdx(i, pNumItemsPerPage);
					if ((pageObj.pageNum > 0) && (pageObj.pageTopIdx > -1))
					{
						retObj.pageNum = pageObj.pageNum;
						retObj.pageTopIdx = pageObj.pageTopIdx;
					}
					break;
				}
			}
		}
	}
	else
	{
		// Go through the message group list and look for a match
		for (var i = pStartItemIdx; i < msg_area.grp_list.length; ++i)
		{
			if ((msg_area.grp_list[i].name.toUpperCase().indexOf(srchText) > -1) ||
			    (msg_area.grp_list[i].description.toUpperCase().indexOf(srchText) > -1))
			{
				retObj.itemIdx = i;
				// Figure out the page number and top index for the page
				var pageObj = calcPageNumAndTopPageIdx(i, pNumItemsPerPage);
				if ((pageObj.pageNum > 0) && (pageObj.pageTopIdx > -1))
				{
					retObj.pageNum = pageObj.pageNum;
					retObj.pageTopIdx = pageObj.pageTopIdx;
				}
				break;
			}
		}
	}

	return retObj;
}

// Calculates the page number (1-based) and top index for the page (0-based),
// given an item index.
//
// Parameters:
//  pItemIdx: The index of the item
//  pNumItemsPerPage: The number of items per page
//
// Return value: An object containing the following properties:
//               pageNum: The page number of the item (1-based; will be 0 if not found)
//               pageTopIdx: The index of the top item on the page (or -1 if not found)
function calcPageNumAndTopPageIdx(pItemIdx, pNumItemsPerPage)
{
	var retObj = {
		pageNum: 0,
		pageTopIdx: -1
	};

	var pageNum = 1;
	var topIdx = 0;
	var continueOn = true;
	do
	{
		var endIdx = topIdx + pNumItemsPerPage;
		if ((pItemIdx >= topIdx) && (pItemIdx < endIdx))
		{
			continueOn = false;
			retObj.pageNum = pageNum;
			retObj.pageTopIdx = topIdx;
		}
		else
		{
			++pageNum;
			topIdx = endIdx;
		}
	} while (continueOn);

	return retObj;
}

// Gets the header of the latest readable message in a sub-board,
// given a number of messages to look at.
//
// Paramters:
//  pSubCode: The internal code of the message sub-board
//  pNumMsgsToCheck: The number of messages to check at the end of the sub-board.
//                   This is optional and if omitted, all messages will be
//                   checked (from the last message) for the latest readable
//                   message header.
//
// Return value: The message header of the latest readable message.  If
//               none is found, this will be null.
function getLatestMsgHdr(pSubCode, pNumMsgsToCheck)
{
	var msgHdr = null;
	var msgBase = new MsgBase(pSubCode);
	if (msgBase.open())
	{
		msgHdr = getLatestMsgHdrWithMsgbase(msgBase, pNumMsgsToCheck);
		msgBase.close();
	}
	delete msgBase; // Free some memory?
	return msgHdr;
}
// Gets the header of the latest readable message in a sub-board,
// given a number of messages to look at.
//
// Paramters:
//  pMsgbase: A MsgBase object for the sub-board, already opened
//  pNumMsgsToCheck: The number of messages to check at the end of the sub-board.
//                   This is optional and if omitted, all messages will be
//                   checked (from the last message) for the latest readable
//                   message header.
//
// Return value: The message header of the latest readable message.  If
//               none is found, this will be null.
function getLatestMsgHdrWithMsgbase(pMsgbase, pNumMsgsToCheck)
{
	if (typeof(pMsgbase) !== "object")
		return null;
	if (!pMsgbase.is_open)
		return null;

	var msgHdr = null;
	var numMsgsToCheck = (typeof(pNumMsgsToCheck) === "number" ? pNumMsgsToCheck : 0);

	// Look through the last numMsgsToCheck headers to find the latest
	// readable message header
	var numMsgs = pMsgbase.total_msgs;
	var firstMsgIdx = 0;
	if (numMsgsToCheck >= 1)
		firstMsgIdx = numMsgs - numMsgsToCheck;
	else
		firstMsgIdx = 0;
	if (firstMsgIdx < 0)
		firstMsgIdx = 0;
	for (var i = numMsgs - 1; (i >= firstMsgIdx) && (msgHdr == null); --i)
	{
		var msgHeader = pMsgbase.get_msg_header(true, i, true);
		if (isReadableMsgHdr(msgHeader, pMsgbase.cfg.code))
			msgHdr = msgHeader;
	}

	return msgHdr;
}

// Gets the time of the latest post in a sub-board.
//
// Parameters:
//  pSubCode: The internal code of the sub-board
//
// Return value: The time of the latest post in the sub-board.  If pSubCode is invalid, this will be 0.
function getLatestMsgTime(pSubCode)
{
	if (typeof(pSubCode) !== "string")
		return 0;

	var latestPostTime = 0;

	var numMsgs = 0;
	var msgBase = new MsgBase(pSubCode);
	if (msgBase.open())
	{
		if (gUseNumReadableMessagesForMsgCount)
			numMsgs = numReadableMsgs(msgBase, pSubCode);
		else
			numMsgs = msgBase.total_msgs;
		msgBase.close();
	}
	delete msgBase; // Free some memory?
	if (numMsgs > 0)
	{
		// Get the latest post time from this sub-board & compare it to retObj.newestTime and
		// set if necessary
		var msgHeader = getLatestMsgHdr(pSubCode);
		if (msgHeader === null)
			msgHeader = getBogusMsgHdr();
		if (this.showImportDates)
			latestPostTime = msgHeader.when_imported_time;
			var msgWrittenLocalBBSTime = msgWrittenTimeToLocalBBSTime(msgHeader);
			latestPostTime = msgWrittenLocalBBSTime != -1 ? msgWrittenLocalBBSTime : msgHeader.when_written_time;
}

// Finds a message group index with search text, matching either the name or
// description, case-insensitive.
//
// Parameters:
//  pSearchText: The name/description text to look for
//  pStartItemIdx: The item index to start at.  Defaults to 0
//
// Return value: The index of the message group, or -1 if not found
function DDMsgAreaChooser_FindMsgGrpIdxFromText(pSearchText, pStartItemIdx)
{
	if (typeof(pSearchText) != "string")
		return -1;

	var grpIdx = -1;

	var startIdx = (typeof(pStartItemIdx) === "number" ? pStartItemIdx : 0);
	if (this.useSubCollapsing)
	{
		if ((startIdx < 0) || (startIdx > this.group_list.length))
			startIdx = 0;
	}
	else
	{
		if ((startIdx < 0) || (startIdx > msg_area.grp_list.length))
			startIdx = 0;
	}

	// Go through the message group list and look for a match
	var searchTextUpper = pSearchText.toUpperCase();
		for (var i = startIdx; i < this.group_list.length; ++i)
			if ((this.group_list[i].name.toUpperCase().indexOf(searchTextUpper) > -1) ||
				(this.group_list[i].description.toUpperCase().indexOf(searchTextUpper) > -1))
			{
				grpIdx = i;
				break;
			}
		}
	}
	else
	{
		for (var i = startIdx; i < msg_area.grp_list.length; ++i)
		{
			if ((msg_area.grp_list[i].name.toUpperCase().indexOf(searchTextUpper) > -1) ||
				(msg_area.grp_list[i].description.toUpperCase().indexOf(searchTextUpper) > -1))
			{
				grpIdx = i;
				break;
			}
// For the DDMsgAreaChooser class: Finds a message group index with search text, matching either the name or
// description, case-insensitive.
//
// Parameters:
//  pGrpIdx: The index of the message group
//  pSubIdx: Optional - The index of the sub-board, for sub-board name collapsing.
//           If this is null, then sub-board collapsing won't be used.
//  pSearchText: The name/description text to look for
//  pStartItemIdx: The item index to start at.  Defaults to 0
//
// Return value: The index of the sub-board, or -1 if not found
function DDMsgAreaChooser_FindSubBoardIdxFromText(pGrpIdx, pSubIdx, pSearchText, pStartItemIdx)
		return -1;
	if (typeof(pSearchText) != "string")
		return -1;

	var subBoardIdx = -1;

	var startIdx = (typeof(pStartItemIdx) === "number" ? pStartItemIdx : 0);
	if (this.useSubCollapsing)
	{
		if (typeof(pSubIdx) === "number")
		{
			if ((startIdx < 0) || (startIdx > this.group_list[pGrpIdx].sub_list[pSubIdx].sub_subboard_list.length))
				startIdx = 0;
		}
		else
		{
			if ((startIdx < 0) || (startIdx > this.group_list[pGrpIdx].sub_list.length))
				startIdx = 0;
		}
	}
	else
	{
		if ((startIdx < 0) || (startIdx > msg_area.grp_list[pGrpIdx].sub_list.length))
			startIdx = 0;
	}
	// Go through the message sub-board list in the group (or sub-subboard list in the sub-board)
	// and look for a match
	var searchTextUpper = pSearchText.toUpperCase();
	if (this.useSubCollapsing)
	{
		if (typeof(pSubIdx) === "number")
		{
			// Look through the sub-subboards of the sub-board
			for (var i = startIdx; i < this.group_list[pGrpIdx].sub_list[pSubIdx].sub_subboard_list.length; ++i)
			{
				// For the sub-subboards, there is only a description, no name
				if (this.group_list[pGrpIdx].sub_list[pSubIdx].sub_subboard_list[i].description.toUpperCase().indexOf(searchTextUpper) > -1)
				{
					subBoardIdx = i;
					break;
				}
			}
		}
		else
		{
			// Look  through the sub-boards in the group
			for (var i = startIdx; i < this.group_list[pGrpIdx].sub_list.length; ++i)
			{
				if ((this.group_list[pGrpIdx].sub_list[i].name.toUpperCase().indexOf(searchTextUpper) > -1) ||
				    (this.group_list[pGrpIdx].sub_list[i].description.toUpperCase().indexOf(searchTextUpper) > -1))
				{
					subBoardIdx = i;
					break;
				}
			}
		}
	}
	else
		for (var i = startIdx; i < msg_area.grp_list[pGrpIdx].sub_list.length; ++i)
			if ((msg_area.grp_list[pGrpIdx].sub_list[i].name.toUpperCase().indexOf(searchTextUpper) > -1) ||
				(msg_area.grp_list[pGrpIdx].sub_list[i].description.toUpperCase().indexOf(searchTextUpper) > -1))
			{
				subBoardIdx = i;
				break;
			}

// For the DDMsgAreaChooser class: Gets the lengths for the sub-board column and # of messages
// column for a sub-board.  Gets defaults if that information isn't available.
//
// Parameters:
//  pGrpIdx: The message group index
//
// Return value: An object containing the following properties:
//                      nameLen: The name field lengh
//                      numMsgsLen: The # messages field length
function DDMsgAreaChooser_GetSubNameLenAndNumMsgsLen(pGrpIdx)
{
	var retObj = {
		nameLen: console.screen_columns - this.areaNumLen - this.numItemsLen - 5,
		numMsgsLen: this.numItemsLen
	};

	if (typeof(pGrpIdx) === "number" && pGrpIdx >= 0 && pGrpIdx < msg_area.grp_list.length)
	{
		if (typeof(this.subBoardListPrintfInfo[pGrpIdx]) === "object")
		{
			if (this.subBoardListPrintfInfo[pGrpIdx].hasOwnProperty("nameLen"))
				retObj.nameLen = +(this.subBoardListPrintfInfo[pGrpIdx].nameLen);
			if (this.subBoardListPrintfInfo[pGrpIdx].hasOwnProperty("numMsgsLen"))
				retObj.numMsgsLen = +(this.subBoardListPrintfInfo[pGrpIdx].numMsgsLen);
		}
	}
	return retObj;
}