Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* This is a script that lets the user choose a message area,
* with either a lightbar or traditional user interface.
*
* Date User Version Description
* 2010-02-05 Eric Oulashin 0.90 Started
* 2010-02-18 to
* 2010-02-27 Eric Oulashin Continued work.
* Added the first & lasg page functionality
* to the lightbar interface.
* 2010-03-13 Eric Oulashin 1.00 Added the ability to load settings from a
* configuration file.
* 2011-04-22 Eric Oulashin 1.01 Fixed the wording when choosing a message
* group - It now says "group #" instead
* of "sub-board #".
* 2012-10-06 Eric Oulashin 1.02 For lightbar mode, updated to display the
* page number in the header at the top (in
* addition to the total number of pages,
* which it was already displaying).
* 2012-11-30 Eric Oulashin 1.03 Bug fix: After leaving the help screen
* from the sub-board list, the top line is
* now correctly written with the page
* information as "Page # of #".
* 2013-05-04 Eric Oulashin 1.04 Updated to dynamically adjust the length
* of the # messages column based on the
* greatest number of messages of all
* sub-boards within a message group so
* that the formatting still looks good.
* 2013-05-10 Eric Oulashin 1.05 Updated the version to match the
* version in DDFileAreaChooser (a bug
* was fixed there, but DDMsgAreaChooser
* didn't have the corresponding bug).
* 2014-09-14 Eric Oulashin 1.06 Bug fix: Updated the highlight (lightbar)
* format string in the
* DDMsgAreaChooser_buildSubBoardPrintfInfoForGrp()
* function to include a normal attribute at
* the end to avoid color issues when clearing
* the screen, etc. Bug reported by Psi-Jack.
* 2014-12-22 Eric Oulashin 1.07 Bug fix: Made this.colors.subBoardHeader apply
* to the whole line rather than just the page
* number.
* Bug fix: The initial display of the page number
* is now correct (previously, it would start out
* saying page 1, even if on another page).
* 2015-04-19 Eric Oulashin 1.08 Added color settings for the lightbar help text
* at the bottom of the screen. Also, added the
* ability to use the PageUp & PageDown keys instead
* of P and N in the lightbar lists.
* 2016-01-17 Eric Oulashin 1.09 Updated to allow choosing only the sub-board within
* the user's current message group. The first command-
* line argument now specifies whether or not to
* allow choosing the message group, and it defaults
* to true.
* 2016-02-12 Eric Oulashin 1.10Beta Started working on adding the ability to display
* a header ANSI/ASCII file above the list.
* 2016-02-15 Eric Oulashin 1.10 Releasing this version
* 2016-02-19 Eric Oulashin 1.11 Bug fix: The page number wasn't being updated
* when changing pages in the message groups
* when using the arrow keys to scroll between
* pages
* 2016-11-20 Eric Oulashin 1.12 Started working on updating to handle null
* message headers, which could happen with the
* new voting feature in Synchronet 3.17.
* 2016-11-22 Eric Oulashin 1.12 Releasing this version
* 2016-12-11 Eric Oulashin 1.13 Updated to show the number of readable messages rather than
* the actual total number of messages in the sub-boards (in
* case some messages are deleted, unverified, etc.)
* 2017-12-18 Eric Oulashin 1.15 Updated the definitions of the KEY_PAGE_UP and KEY_PAGE_DOWN
* variables to match what they are in sbbsdefs.js (if defined)
* from December 18, 2017 so that the PageUp and PageDown keys
* continue to work properly. This script should still also work
* with older builds of Synchronet.
* 2018-03-09 Eric Oulashin 1.16B Bug fix for off-by-one when a message group has no sub-boards.
* 2018-06-25 Eric Oulashin 1.17 Added a new configuration file option, showDatesInSubBoardList,
* that specifies whether or not to show the date & time of
* the latest message in the sub-boards.
* 2019-08-08 Eric Oulashin 1.18 Beta Started working on message area searching.
* Also, improved the time to display sub-boards
* with the latest message date & time.
* 2019-08-22 Eric Oulashin 1.18 Releasing this version
* 2019-08-24 Eric Oulashin 1.19 Fixed a bug with 'Next' search when
* returning from sub-board choosing
*
*/
/* Command-line arguments:
1 (argv[0]): Boolean - Whether or not to choose a message group first (default). If
false, the user will only be able to choose a different sub-board within
their current message group.
2 (argv[1]): Boolean - Whether or not to run the area chooser (if false,
then this file will just provide the DDMsgAreaChooser class).
*/
var requireFnExists = (typeof(require) === "function");
if (requireFnExists)
require("sbbsdefs.js", "K_NOCRLF");
else
load("sbbsdefs.js");
// This script requires Synchronet version 3.14 or higher.
// Exit if the Synchronet version is below the minimum.
if (system.version_num < 31400)
{
var message = "\1n\1h\1y\1i* Warning:\1n\1h\1w Digital Distortion Message Lister "
+ "requires version \1g3.14\1w or\r\n"
+ "higher of Synchronet. This BBS is using version \1g" + system.version
+ "\1w. Please notify the sysop.";
console.crlf();
console.print(message);
console.crlf();
console.pause();
exit();
}
// Version & date variables
var DD_MSG_AREA_CHOOSER_VERSION = "1.19";
var DD_MSG_AREA_CHOOSER_VER_DATE = "2019-08-24";
// Keyboard input key codes
var CTRL_H = "\x08";
var CTRL_M = "\x0d";
var KEY_ENTER = CTRL_M;
var BACKSPACE = CTRL_H;
var CTRL_F = "\x06";
var KEY_ESC = ascii(27);
// PageUp & PageDown keys - Synchronet 3.17 as of about December 18, 2017
// use CTRL-P and CTRL-N for PageUp and PageDown, respectively. sbbsdefs.js
// defines them as KEY_PAGEUP and KEY_PAGEDN; I've used slightly different names
// in this script so that this script will work with Synchronet systems before
// and after the update containing those key definitions.
var KEY_PAGE_UP = "\x10"; // Ctrl-P
var KEY_PAGE_DOWN = "\x0e"; // Ctrl-N
// Ensure KEY_PAGE_UP and KEY_PAGE_DOWN are set to what's defined in sbbs.js
// for KEY_PAGEUP and KEY_PAGEDN in case they change
if (typeof(KEY_PAGEUP) === "string")
KEY_PAGE_UP = KEY_PAGEUP;
if (typeof(KEY_PAGEDN) === "string")
KEY_PAGE_DOWN = KEY_PAGEDN;
// Key codes for display
var UP_ARROW = ascii(24);
var DOWN_ARROW = ascii(25);
// Misc. defines
var ERROR_WAIT_MS = 1500;
var SEARCH_TIMEOUT_MS = 10000;
// Determine the script's startup directory.
// This code is a trick that was created by Deuce, suggested by Rob Swindell
// as a way to detect which directory the script was executed in. I've
// shortened the code a little.
var gStartupPath = '.';
try { throw dig.dist(dist); } catch(e) { gStartupPath = e.fileName; }
gStartupPath = backslash(gStartupPath.replace(/[\/\\][^\/\\]*$/,''));
// gIsSysop stores whether or not the user is a sysop.
var gIsSysop = user.compare_ars("SYSOP"); // Whether or not the user is a sysop
// 1st command-line argument: Whether or not to choose a message group first (if
// false, then only choose a sub-board within the user's current group). This
// can be true or false.
var chooseMsgGrp = true;
if (typeof(argv[0]) == "boolean")
chooseMsgGrp = argv[0];
else if (typeof(argv[0]) == "string")
chooseMsgGrp = (argv[0].toLowerCase() == "true");
// 2nd command-line argument: Determine whether or not to execute the message listing
// code (true/false)
var executeThisScript = true;
if (typeof(argv[1]) == "boolean")
executeThisScript = argv[1];
else if (typeof(argv[1]) == "string")
executeThisScript = (argv[1].toLowerCase() == "true");
// If executeThisScript is true, then create a DDMsgAreaChooser object and use
// it to let the user choose a message area.
if (executeThisScript)
{
var msgAreaChooser = new DDMsgAreaChooser();
msgAreaChooser.SelectMsgArea(chooseMsgGrp);
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
}
// End of script execution
///////////////////////////////////////////////////////////////////////////////////
// DDMsgAreaChooser class stuff
function DDMsgAreaChooser()
{
// this.colors will be an associative array of colors (indexed by their
// usage) used for the message group/sub-board lists.
// Colors for the file & message area lists
this.colors = new Object();
this.colors.areaNum = "\1n\1w\1h";
this.colors.desc = "\1n\1c";
this.colors.numItems = "\1b\1h";
this.colors.header = "\1n\1y\1h";
this.colors.subBoardHeader = "\1n\1g";
this.colors.areaMark = "\1g\1h";
this.colors.latestDate = "\1n\1g";
this.colors.latestTime = "\1n\1m";
// Highlighted colors (for lightbar mode)
this.colors.bkgHighlight = "\1" + "4"; // Blue background
this.colors.areaNumHighlight = "\1w\1h";
this.colors.descHighlight = "\1c";
this.colors.dateHighlight = "\1w\1h";
this.colors.timeHighlight = "\1w\1h";
this.colors.numItemsHighlight = "\1w\1h";
// Lightbar help line colors
this.colors.lightbarHelpLineBkg = "\1" + "7";
this.colors.lightbarHelpLineGeneral = "\1b";
this.colors.lightbarHelpLineHotkey = "\1r";
this.colors.lightbarHelpLineParen = "\1m";
// showImportDates is a boolean to specify whether or not to display the
// message import dates. If false, the message written dates will be
// displayed instead.
this.showImportDates = true;
// useLightbarInterface specifies whether or not to use the lightbar
// interface. The lightbar interface will still only be used if the
// user's terminal supports ANSI.
this.useLightbarInterface = true;
// Filename base of a header to display above the area list
this.areaChooserHdrFilenameBase = "msgAreaChgHeader";
this.areaChooserHdrMaxLines = 5;
// Whether or not to show the latest message date/time in the
// sub-board list
this.showDatesInSubBoardList = true;
// Set the function pointers for the object
this.ReadConfigFile = DDMsgAreaChooser_ReadConfigFile;
this.WriteKeyHelpLine = DDMsgAreaChooser_writeKeyHelpLine;
this.WriteGrpListHdrLine = DDMsgAreaChooser_writeGrpListTopHdrLine;
this.WriteSubBrdListHdr1Line = DMsgAreaChooser_writeSubBrdListHdr1Line;
this.SelectMsgArea = DDMsgAreaChooser_selectMsgArea;
this.SelectMsgArea_Lightbar = DDMsgAreaChooser_selectMsgArea_Lightbar;
this.SelectSubBoard_Lightbar = DDMsgAreaChooser_selectSubBoard_Lightbar;
this.SelectMsgArea_Traditional = DDMsgAreaChooser_selectMsgArea_Traditional;
this.SelectSubBoard_Traditional = DDMsgAreaChooser_selectSubBoard_Traditional;
this.ListMsgGrps = DDMsgAreaChooser_listMsgGrps_Traditional;
this.ListSubBoardsInMsgGroup = DDMsgAreaChooser_listSubBoardsInMsgGroup_Traditional;
// Lightbar-specific functions
this.ListScreenfulOfMsgGrps = DDMsgAreaChooser_listScreenfulOfMsgGrps;
this.WriteMsgGroupLine = DDMsgAreaChooser_writeMsgGroupLine;
this.updatePageNumInHeader = DDMsgAreaChooser_updatePageNumInHeader;
this.ListScreenfulOfSubBrds = DDMsgAreaChooser_listScreenfulOfSubBrds;
this.WriteMsgSubBoardLine = DDMsgAreaChooser_writeMsgSubBrdLine;
// Help screen
this.ShowHelpScreen = DDMsgAreaChooser_showHelpScreen;
// Function to build the sub-board printf information for a message
// group
this.BuildSubBoardPrintfInfoForGrp = DDMsgAreaChooser_buildSubBoardPrintfInfoForGrp;
this.DisplayAreaChgHdr = DDMsgAreaChooser_DisplayAreaChgHdr;
this.WriteLightbarKeyHelpErrorMsg = DDMsgAreaChooser_WriteLightbarKeyHelpErrorMsg;
// Read the settings from the config file.
this.ReadConfigFile();
// These variables store the lengths of the various columns displayed in
// the message group/sub-board lists.
// Sub-board info field lengths
this.areaNumLen = 4;
this.numItemsLen = 4;
this.dateLen = 10; // i.e., YYYY-MM-DD
this.timeLen = 8; // i.e., HH:MM:SS
// Sub-board name length - This should be 47 for an 80-column display.
this.subBoardNameLen = console.screen_columns - this.areaNumLen - this.numItemsLen - 5;
if (this.showDatesInSubBoardList)
this.subBoardNameLen -= (this.dateLen + this.timeLen + 2);
// Message group description length (67 chars on an 80-column screen)
this.msgGrpDescLen = console.screen_columns - this.areaNumLen - this.numItemsLen - 5;
// printf strings for various things
// Message group information (printf strings)
this.msgGrpListPrintfStr = "\1n " + this.colors.areaNum + "%" + this.areaNumLen
+ "d " + this.colors.desc + "%-"
+ this.msgGrpDescLen + "s " + this.colors.numItems
+ "%" + this.numItemsLen + "d";
this.msgGrpListHilightPrintfStr = "\1n" + this.colors.bkgHighlight + " "
+ "\1n" + this.colors.bkgHighlight
+ this.colors.areaNumHighlight + "%" + this.areaNumLen
+ "d \1n" + this.colors.bkgHighlight
+ this.colors.descHighlight + "%-"
+ this.msgGrpDescLen + "s \1n" + this.colors.bkgHighlight
+ this.colors.numItemsHighlight + "%" + this.numItemsLen
+ "d";
// Message group list header (printf string)
this.msgGrpListHdrPrintfStr = this.colors.header + "%6s %-"
+ +(this.msgGrpDescLen-8) + "s %-12s";
// Sub-board information header (printf string)
this.subBoardListHdrPrintfStr = this.colors.header + " %5s %-"
+ +(this.subBoardNameLen-3) + "s %-7s";
if (this.showDatesInSubBoardList)
this.subBoardListHdrPrintfStr += " %-19s";
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
// Lightbar mode key help line
this.lightbarKeyHelpText = "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + UP_ARROW
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + DOWN_ARROW
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "HOME"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "END"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "#"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "PgUp"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + "/"
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "Dn"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "F"
+ "\1n" + this.colors.lightbarHelpLineParen
+ this.colors.lightbarHelpLineBkg + ")"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + "irst pg, "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "L"
+ "\1n" + this.colors.lightbarHelpLineParen
+ this.colors.lightbarHelpLineBkg + ")"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + "ast pg, "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "CTRL-F"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "/"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "N"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + ", "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "Q"
+ "\1n" + this.colors.lightbarHelpLineParen
+ this.colors.lightbarHelpLineBkg + ")"
+ "\1n" + this.colors.lightbarHelpLineGeneral
+ this.colors.lightbarHelpLineBkg + "uit, "
+ "\1n" + this.colors.lightbarHelpLineHotkey
+ this.colors.lightbarHelpLineBkg + "?";
// Pad the lightbar key help text on either side to center it on the screen
// (but leave off the last character to avoid screen drawing issues)
var helpTextLen = console.strlen(this.lightbarKeyHelpText);
var helpTextStartCol = (console.screen_columns/2) - (helpTextLen/2);
this.lightbarKeyHelpText = "\1n" + this.colors.lightbarHelpLineBkg
+ format("%" + +(helpTextStartCol) + "s", "")
+ this.lightbarKeyHelpText + "\1n"
+ this.colors.lightbarHelpLineBkg;
var numTrailingChars = console.screen_columns - (helpTextStartCol+helpTextLen) - 1;
this.lightbarKeyHelpText += format("%" + +(numTrailingChars) + "s", "") + "\1n";
// this.subBoardListPrintfInfo will be an array of printf strings
// for the sub-boards in the message groups. The index is the
// message group index. The sub-board printf information is created
// on the fly the first time the user lists sub-boards for a message
// group.
this.subBoardListPrintfInfo = new Array();
// areaChangeHdrLines is an array of text lines to use as a header to display
// above the message area changer lists.
this.areaChangeHdrLines = loadTextFileIntoArray(this.areaChooserHdrFilenameBase, this.areaChooserHdrMaxLines);
}
// For the DDMsgAreaChooser class: Writes the line of key help at the bottom
// row of the screen.
function DDMsgAreaChooser_writeKeyHelpLine()
{
console.gotoxy(1, console.screen_rows);
console.print(this.lightbarKeyHelpText);
}
// For the DDMsgAreaChooser class: Outputs the header line to appear above
// the list of message groups.
//
// Parameters:
// pNumPages: The number of pages. This is optional; if this is
// not passed, then it won't be used.
// pPageNum: The page number. This is optional; if this is not passed,
// then it won't be used.
function DDMsgAreaChooser_writeGrpListTopHdrLine(pNumPages, pPageNum)
{
var descStr = "Description";
if ((typeof(pPageNum) == "number") && (typeof(pNumPages) == "number"))
descStr += " (Page " + pPageNum + " of " + pNumPages + ")";
else if ((typeof(pPageNum) == "number") && (typeof(pNumPages) != "number"))
descStr += " (Page " + pPageNum + ")";
else if ((typeof(pPageNum) != "number") && (typeof(pNumPages) == "number"))
descStr += " (" + pNumPages + (pNumPages == 1 ? " page)" : " pages)");
printf(this.msgGrpListHdrPrintfStr, "Group#", descStr, "# Sub-Boards");
console.cleartoeol("\1n");
}
// For the DDMsgAreaChooser class: Outputs the first header line to appear
// above the sub-board list for a message group.
//
// Parameters:
// pGrpIndex: The index of the message group (assumed to be valid)
// pNumPages: The number of pages. This is optional; if this is
// not passed, then it won't be used.
// pPageNum: The page number. This is optional; if this is not passed,
// then it won't be used.
function DMsgAreaChooser_writeSubBrdListHdr1Line(pGrpIndex, pNumPages, pPageNum)
{
var descFormatStr = "\1n" + this.colors.subBoardHeader + "Sub-boards of \1h%-25s \1n"
+ this.colors.subBoardHeader;
if ((typeof(pPageNum) == "number") && (typeof(pNumPages) == "number"))
descFormatStr += "(Page " + pPageNum + " of " + pNumPages + ")";
else if ((typeof(pPageNum) == "number") && (typeof(pNumPages) != "number"))
descFormatStr += "(Page " + pPageNum + ")";
else if ((typeof(pPageNum) != "number") && (typeof(pNumPages) == "number"))
descFormatStr += "(" + pNumPages + (pNumPages == 1 ? " page)" : " pages)");
printf(descFormatStr, msg_area.grp_list[pGrpIndex].description.substr(0, 25));
console.cleartoeol("\1n");
}
// For the DDMsgAreaChooser class: Lets the user choose a message group and
// sub-board via numeric input, using a lightbar interface (if enabled and
// if the user's terminal uses ANSI) or a traditional user interface.
//
// Parameters:
// pChooseGroup: Boolean - Whether or not to choose the message group. If false,
// then this will allow choosing a sub-board within the user's
// current message group. This is optional; defaults to true.
function DDMsgAreaChooser_selectMsgArea(pChooseGroup)
{
if (this.useLightbarInterface && console.term_supports(USER_ANSI))
this.SelectMsgArea_Lightbar(pChooseGroup);
else
this.SelectMsgArea_Traditional(pChooseGroup);
}
// For the DDMsgAreaChooser class: Lets the user choose a message group and
// sub-board via numeric input, using a lightbar user interface.
//
// Parameters:
// pChooseGroup: Boolean - Whether or not to choose the message group. If false,
// then this will allow choosing a sub-board within the user's
// current message group. This is optional; defaults to true.
function DDMsgAreaChooser_selectMsgArea_Lightbar(pChooseGroup)
{
// If there are no message groups, then don't let the user
// choose one.
if (msg_area.grp_list.length == 0)
{
console.clear("\1n");
console.print("\1y\1hThere are no message groups.\r\n\1p");
return;
}
var chooseGroup = (typeof(pChooseGroup) == "boolean" ? pChooseGroup : true);
if (chooseGroup)
{
// Returns the index of the bottommost message group that can be displayed
// on the screen.
//
// Parameters:
// pTopGrpIndex: The index of the topmost message group displayed on screen
// pNumItemsPerPage: The number of items per page
function getBottommostGrpIndex(pTopGrpIndex, pNumItemsPerPage)
{
var bottomGrpIndex = pTopGrpIndex + pNumItemsPerPage - 1;
// If bottomGrpIndex is beyond the last index, then adjust it.
if (bottomGrpIndex >= msg_area.grp_list.length)
bottomGrpIndex = msg_area.grp_list.length - 1;
return bottomGrpIndex;
}
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
// For doing the "next" search result
function nextGrpSearchFoundItem(searchObj, numPages, listStartRow, listEndRow, selectedGrpIndex, topMsgGrpIndex, chooserObj)
{
var retObj = {
differentPage: false,
topMsgGrpIndex: srchObj.pageTopIdx,
selectedGrpIndex: srchObj.itemIdx
};
// For screen refresh optimization, don't redraw the whole
// list if the result is on the same page
if (srchObj.pageTopIdx != topMsgGrpIndex)
{
retObj.differentPage = true;
chooserObj.updatePageNumInHeader(srchObj.pageNum, numPages, true, false);
chooserObj.ListScreenfulOfMsgGrps(retObj.topMsgGrpIndex, listStartRow, listEndRow, false, true, srchObj.itemIdx);
}
else
{
if (srchObj.itemIdx != selectedGrpIndex)
{
var screenY = listStartRow + (selectedGrpIndex - topMsgGrpIndex);
console.gotoxy(1, screenY);
chooserObj.WriteMsgGroupLine(selectedGrpIndex, false);
retObj.selectedGrpIndex = srchObj.itemIdx;
screenY = listStartRow + (retObj.selectedGrpIndex - topMsgGrpIndex);
console.gotoxy(1, screenY);
chooserObj.WriteMsgGroupLine(retObj.selectedGrpIndex, true);
}
}
return retObj;
}
// Figure out the index of the user's currently-selected message group
var selectedGrpIndex = 0;
if ((typeof(bbs.cursub_code) == "string") && (bbs.cursub_code != ""))
selectedGrpIndex = msg_area.sub[bbs.cursub_code].grp_index;
var listStartRow = 2 + this.areaChangeHdrLines.length; // The row on the screen where the list will start
var listEndRow = console.screen_rows - 1; // Row on screen where list will end
var topMsgGrpIndex = 0; // The index of the message group at the top of the list
// Figure out the index of the last message group to appear on the screen.
var numItemsPerPage = listEndRow - listStartRow + 1;
var bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
// Figure out how many pages are needed to list all the sub-boards.
var numPages = Math.ceil(msg_area.grp_list.length / numItemsPerPage);
// Figure out the top index for the last page.
var topIndexForLastPage = (numItemsPerPage * numPages) - numItemsPerPage;
// If the highlighted row is beyond the current screen, then
// go to the appropriate page.
if (selectedGrpIndex > bottomMsgGrpIndex)
{
var nextPageTopIndex = 0;
while (selectedGrpIndex > bottomMsgGrpIndex)
{
nextPageTopIndex = topMsgGrpIndex + numItemsPerPage;
if (nextPageTopIndex < msg_area.grp_list.length)
{
// Adjust topMsgGrpIndex and bottomMsgGrpIndex, and
// refresh the list on the screen.
topMsgGrpIndex = nextPageTopIndex;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
}
else
break;
}
// If we didn't find the correct page for some reason, then set the
// variables to display page 1 and select the first message group.
var foundCorrectPage = ((topMsgGrpIndex < msg_area.grp_list.length) &&
(selectedGrpIndex >= topMsgGrpIndex) && (selectedGrpIndex <= bottomMsgGrpIndex));
if (!foundCorrectPage)
{
topMsgGrpIndex = 0;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
selectedGrpIndex = 0;
}
}
// Clear the screen, write the header, help line, and group list header, and output
// a screenful of message groups.
console.clear("\1n");
this.DisplayAreaChgHdr(1);
this.WriteKeyHelpLine();
var curpos = {
x: 1,
y: 1 + this.areaChangeHdrLines.length
};
console.gotoxy(curpos);
var pageNum = calcPageNum(topMsgGrpIndex, numItemsPerPage);
this.WriteGrpListHdrLine(numPages, pageNum);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, false);
// Start of the input loop.
var highlightScrenRow = 0; // The row on the screen for the highlighted group
var userInput = ""; // Will store a keypress from the user
var retObj = null; // To store the return value of choosing a sub-board
var lastSearchText = "";
var lastSearchFoundIdx = -1;
var continueChoosingMsgArea = true;
while (continueChoosingMsgArea)
{
// Highlight the currently-selected message group
highlightScrenRow = listStartRow + (selectedGrpIndex - topMsgGrpIndex);
curpos.y = highlightScrenRow;
if ((highlightScrenRow > 0) && (highlightScrenRow < console.screen_rows))
{
console.gotoxy(1, highlightScrenRow);
this.WriteMsgGroupLine(selectedGrpIndex, true);
}
// Get a key from the user (upper-case) and take action based upon it.
userInput = getKeyWithESCChars(K_UPPER | K_NOCRLF);
switch (userInput)
{
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
case KEY_UP: // Move up one message group in the list
if (selectedGrpIndex > 0)
{
// If the previous group index is on the previous page, then
// display the previous page.
var previousGrpIndex = selectedGrpIndex - 1;
if (previousGrpIndex < topMsgGrpIndex)
{
// Adjust topMsgGrpIndex and bottomMsgGrpIndex, and
// refresh the list on the screen.
topMsgGrpIndex -= numItemsPerPage;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
}
else
{
// Display the current line un-highlighted.
console.gotoxy(1, curpos.y);
this.WriteMsgGroupLine(selectedGrpIndex, false);
}
selectedGrpIndex = previousGrpIndex;
}
break;
case KEY_DOWN: // Move down one message group in the list
if (selectedGrpIndex < msg_area.grp_list.length - 1)
{
// If the next group index is on the next page, then display
// the next page.
var nextGrpIndex = selectedGrpIndex + 1;
if (nextGrpIndex > bottomMsgGrpIndex)
{
// Adjust topMsgGrpIndex and bottomMsgGrpIndex, and
// refresh the list on the screen.
topMsgGrpIndex += numItemsPerPage;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum+1, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow,
listEndRow, false, true);
}
else
{
// Display the current line un-highlighted.
// TODO: Something is wrong with curpos.y here if
// you do a search and press next, especially after
// going to the next page
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
console.gotoxy(1, curpos.y);
this.WriteMsgGroupLine(selectedGrpIndex, false);
}
selectedGrpIndex = nextGrpIndex;
}
break;
case KEY_HOME: // Go to the top message group on the screen
if (selectedGrpIndex > topMsgGrpIndex)
{
// Display the current line un-highlighted, then adjust
// selectedGrpIndex.
console.gotoxy(1, curpos.y);
this.WriteMsgGroupLine(selectedGrpIndex, false);
selectedGrpIndex = topMsgGrpIndex;
// Note: curpos.y is set at the start of the while loop.
}
break;
case KEY_END: // Go to the bottom message group on the screen
if (selectedGrpIndex < bottomMsgGrpIndex)
{
// Display the current line un-highlighted, then adjust
// selectedGrpIndex.
console.gotoxy(1, curpos.y);
this.WriteMsgGroupLine(selectedGrpIndex, false);
selectedGrpIndex = bottomMsgGrpIndex;
// Note: curpos.y is set at the start of the while loop.
}
break;
case KEY_ENTER: // Select the currently-highlighted message group
retObj = this.SelectSubBoard_Lightbar(selectedGrpIndex);
// If the user chose a sub-board, then set the user's message
// sub-board, and don't continue the input loop anymore.
if (retObj.subBoardChosen)
{
continueChoosingMsgArea = false;
bbs.cursub_code = retObj.subBoardCode;
}
else
{
// A sub-board was not chosen, so we'll have to re-draw
// the header and list of message groups.
this.DisplayAreaChgHdr(1);
console.gotoxy(1, 1+this.areaChangeHdrLines.length);
this.WriteGrpListHdrLine(numPages, pageNum);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
}
break;
case KEY_PAGE_DOWN: // Go to the next page
var nextPageTopIndex = topMsgGrpIndex + numItemsPerPage;
if (nextPageTopIndex < msg_area.grp_list.length)
{
// Adjust topMsgGrpIndex and bottomMsgGrpIndex, and
// refresh the list on the screen.
topMsgGrpIndex = nextPageTopIndex;
pageNum = calcPageNum(topMsgGrpIndex, numItemsPerPage);
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
selectedGrpIndex = topMsgGrpIndex;
}
break;
case KEY_PAGE_UP: // Go to the previous page
var prevPageTopIndex = topMsgGrpIndex - numItemsPerPage;
if (prevPageTopIndex >= 0)
{
// Adjust topMsgGrpIndex and bottomMsgGrpIndex, and
// refresh the list on the screen.
topMsgGrpIndex = prevPageTopIndex;
pageNum = calcPageNum(topMsgGrpIndex, numItemsPerPage);
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
selectedGrpIndex = topMsgGrpIndex;
}
break;
case 'F': // Go to the first page
if (topMsgGrpIndex > 0)
{
topMsgGrpIndex = 0;
pageNum = calcPageNum(topMsgGrpIndex, numItemsPerPage);
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
selectedGrpIndex = 0;
}
break;
case 'L': // Go to the last page
if (topMsgGrpIndex < topIndexForLastPage)
{
topMsgGrpIndex = topIndexForLastPage;
pageNum = calcPageNum(topMsgGrpIndex, numItemsPerPage);
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
selectedGrpIndex = topIndexForLastPage;
}
break;
case 'Q': // Quit
continueChoosingMsgArea = false;
break;
case '?': // Show help
this.ShowHelpScreen(true, true);
console.pause();
// Refresh the screen
this.WriteKeyHelpLine();
this.DisplayAreaChgHdr(1);
console.gotoxy(1, 1+this.areaChangeHdrLines.length);
this.WriteGrpListHdrLine(numPages, pageNum);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
break;
case '/': // Start of find (search)
case CTRL_F: // Start of find
console.gotoxy(1, console.screen_rows);
console.cleartoeol("\1n");
console.gotoxy(1, console.screen_rows);
var promptText = "Search: ";
console.print(promptText);
var searchText = getStrWithTimeout(K_UPPER|K_NOCRLF|K_GETSTR|K_NOSPIN|K_LINE, console.screen_columns - promptText.length - 1, SEARCH_TIMEOUT_MS);
// If the user entered text, then do the search, and if found,
// found, go to the page and select the item indicated by the
// search.
if (searchText.length > 0)
{
var srchObj = getPageNumFromSearch(searchText, numItemsPerPage, false, 0);
if (srchObj.pageNum > 0)
{
lastSearchText = searchText;
lastSearchFoundIdx = srchObj.itemIdx;
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
// For screen refresh optimization, don't redraw the whole
// list if the result is on the same page
if (srchObj.pageTopIdx != topMsgGrpIndex)
{
topMsgGrpIndex = srchObj.pageTopIdx;
selectedGrpIndex = srchObj.itemIdx;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
pageNum = srchObj.pageNum;
this.updatePageNumInHeader(pageNum, numPages, true, false);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
}
else
{
if (srchObj.itemIdx != selectedGrpIndex)
{
var screenY = listStartRow + (selectedGrpIndex - topMsgGrpIndex);
console.gotoxy(1, screenY);
this.WriteMsgGroupLine(selectedGrpIndex, false);
selectedGrpIndex = srchObj.itemIdx;
screenY = listStartRow + (selectedGrpIndex - topMsgGrpIndex);
console.gotoxy(1, screenY);
this.WriteMsgGroupLine(selectedGrpIndex, true);
}
}
}
else
this.WriteLightbarKeyHelpErrorMsg("Not found", false);
}
this.WriteKeyHelpLine();
break;
case 'N': // Next search result (requires an existing search term)
if ((lastSearchText.length > 0) && (lastSearchFoundIdx > -1))
{
// Do the search, and if found, go to the page and select the item
// indicated by the search.
var srchObj = getPageNumFromSearch(lastSearchText, numItemsPerPage, false, lastSearchFoundIdx+1);
lastSearchFoundIdx = srchObj.itemIdx;
if (srchObj.pageNum > 0)
{
var foundItemRetObj = nextGrpSearchFoundItem(srchObj, numPages, listStartRow, listEndRow, selectedGrpIndex, topMsgGrpIndex, this);
if (foundItemRetObj.differentPage)
{
pageNum = srchObj.pageNum;
topMsgGrpIndex = foundItemRetObj.topMsgGrpIndex;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
}
selectedGrpIndex = foundItemRetObj.selectedGrpIndex;
}
else
{
// Not found - Wrap around and start at 0 again
var srchObj = getPageNumFromSearch(lastSearchText, numItemsPerPage, false, 0);
lastSearchFoundIdx = srchObj.itemIdx;
var foundItemRetObj = nextGrpSearchFoundItem(srchObj, numPages, listStartRow, listEndRow, selectedGrpIndex, topMsgGrpIndex, this);
if (foundItemRetObj.selectedGrpIndex != selectedGrpIndex)
{
if (foundItemRetObj.differentPage)
{
pageNum = srchObj.pageNum;
topMsgGrpIndex = foundItemRetObj.topMsgGrpIndex;
bottomMsgGrpIndex = getBottommostGrpIndex(topMsgGrpIndex, numItemsPerPage);
}
selectedGrpIndex = foundItemRetObj.selectedGrpIndex;
}
else
this.WriteLightbarKeyHelpErrorMsg("No others found", true);
}
}
else
this.WriteLightbarKeyHelpErrorMsg("There is no previous search", true);
break;
default:
// If the user entered a numeric digit, then treat it as
// the start of the message group number.
if (userInput.match(/[0-9]/))
{
var originalCurpos = curpos;
// 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);
console.clearline("\1n");
console.print("\1cChoose group #: \1h");
userInput = console.getnum(msg_area.grp_list.length);
// If the user made a selection, then let them choose a
// sub-board from the group.
if (userInput > 0)
{
var msgGroupIndex = userInput - 1;
retObj = this.SelectSubBoard_Lightbar(msgGroupIndex);
// If the user chose a sub-board, then set the user's
// message sub-board, and don't continue the input loop anymore.
if (retObj.subBoardChosen)
{
continueChoosingMsgArea = false;
bbs.cursub_code = retObj.subBoardCode;
}
else
{
// A sub-board was not chosen, so we'll have to re-draw
// the header and list of message groups.
//this.DisplayAreaChgHdr(1);
console.gotoxy(1, 1+this.areaChangeHdrLines.length);
this.WriteGrpListHdrLine(numPages, pageNum);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
}
}
else
{
// The user didn't make a selection. So, we need to refresh
// the screen due to everything being moved up one line.
this.WriteKeyHelpLine();
//this.DisplayAreaChgHdr(1);
console.gotoxy(1, 1+this.areaChangeHdrLines.length);
this.WriteGrpListHdrLine(numPages, pageNum);
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
}
}
break;
}
}
}
else
{
// Don't choose a group, just a sub-board within the user's current group.
var grpIndex = 0;
if ((typeof(bbs.cursub_code) == "string") && (bbs.cursub_code != ""))
grpIndex = msg_area.sub[bbs.cursub_code].grp_index;
retObj = this.SelectSubBoard_Lightbar(grpIndex);
// If the user chose a sub-board, then set the user's sub-board
if (retObj.subBoardChosen)
bbs.cursub_code = retObj.subBoardCode;
}
}
// For the DDMsgAreaChooser class: Lets the user choose a sub-board within a
// message group, with a lightbar interface. Does not set the user's sub-board.
//
// Parameters:
// pGrpIndex: The index of the message group to choose from. This is
// optional; if not specified, the user's current group index will be used.
// pMarkIndex: An index of a message group to display the "current" mark
// next to. This is optional; if left off, this will default to
// the current sub-board.
//
// Return value: An object containing the following values:
// subBoardChosen: Boolean - Whether or not a sub-board was chosen.
// subBoardIndex: Numeric - The sub-board that was chosen (if any).
// Will be -1 if none chosen.
// subBoardCode: The internal code of the chosen sub-board ("" if none chosen)
function DDMsgAreaChooser_selectSubBoard_Lightbar(pGrpIndex, pMarkIndex)
{
// Create the return object.
var retObj = {
subBoardChosen: false,
subBoardIndex: -1,
subBoardCode: ""
};
var usersCurrentIdxVals = getGrpAndSubIdxesFromCode(bbs.cursub_code, true);
var grpIndex = 0;
if (typeof(pGrpIndex) == "number")
grpIndex = pGrpIndex;
else
grpIndex = usersCurrentIdxVals.grpIdx;
// Double-check grpIndex
if (grpIndex < 0)
grpIndex = 0;
else if (grpIndex >= msg_area.grp_list.length)
grpIndex = msg_area.grp_list.length - 1;
var markIndex = 0;
if ((pMarkIndex != null) && (typeof(pMarkIndex) == "number"))
markIndex = pMarkIndex;
else if (pGrpIndex == usersCurrentIdxVals.grpIdx)
markIndex = usersCurrentIdxVals.subIdx;
// Double-check markIndex
if (markIndex < 0)
markIndex = 0;
else if (markIndex >= msg_area.grp_list[grpIndex].sub_list.length)
markIndex = msg_area.grp_list[grpIndex].sub_list.length - 1;
// Ensure that the sub-board printf information is created for
// this message group.
this.BuildSubBoardPrintfInfoForGrp(grpIndex);
// If there are no sub-boards in the given message group, then show
// an error and return.
if (msg_area.grp_list[grpIndex].sub_list.length == 0)
{
console.clear("\1n");
console.print("\1y\1hThere are no sub-boards in the chosen group.\r\n\1p");
return retObj;
}
// Returns the index of the bottommost sub-board that can be displayed on
// the screen.
//
// Parameters:
// pTopSubIndex: The index of the topmost sub-board displayed on screen
// pNumItemsPerPage: The number of items per page
function getBottommostSubIndex(pTopSubIndex, pNumItemsPerPage)
{
var bottomGrpIndex = topSubIndex + pNumItemsPerPage - 1;
// If bottomGrpIndex is beyond the last index, then adjust it.
if (bottomGrpIndex >= msg_area.grp_list[grpIndex].sub_list.length)
bottomGrpIndex = msg_area.grp_list[grpIndex].sub_list.length - 1;
return bottomGrpIndex;
}
// For doing the "next" search result
function nextSubSearchFoundItem(grpIndex, searchObj, numPages, listStartRow, listEndRow, selectedSubIndex, topSubIndex, chooserObj)
{
var retObj = {
differentPage: false,
topSubIndex: srchObj.pageTopIdx,