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
46
47
/* 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.
*/
/* 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).
*/
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.15";
var DD_MSG_AREA_CHOOSER_VER_DATE = "2017-12-18";
// Keyboard input key codes
var CTRL_M = "\x0d";
var KEY_ENTER = CTRL_M;
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);
// 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);
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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
}
// 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;
// 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 - this.dateLen - this.timeLen - 7;
// Message group description length (67 chars on an 80-column screen)
this.msgGrpDescLen = console.screen_columns - this.areaNumLen -
this.numItemsLen - 5;
// Filename base of a header to display above the area list
this.areaChooserHdrFilenameBase = "msgAreaChgHeader";
this.areaChooserHdrMaxLines = 5;
// 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;
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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
// Read the settings from the config file.
this.ReadConfigFile();
// 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 %-19s";
// 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 + "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;
}
// Figure out the index of the user's currently-selected message group
var selectedGrpIndex = 0;
if ((bbs.curgrp != null) && (typeof(bbs.curgrp) == "number"))
selectedGrpIndex = bbs.curgrp;
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 = new Object();
curpos.x = 1;
curpos.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 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)
{
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);
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow,
listEndRow, false, true);
}
else
{
// Display the current line un-highlighted.
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 bbs.curgrp and
// bbs.cursub, and don't continue the input loop anymore.
if (retObj.subBoardChosen)
{
bbs.curgrp = selectedGrpIndex;
bbs.cursub = retObj.subBoardIndex;
continueChoosingMsgArea = false;
}
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);
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
647
648
649
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
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;
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;
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
// 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 bbs.curgrp and
// bbs.cursub, and don't continue the input loop anymore.
if (retObj.subBoardChosen)
{
bbs.curgrp = msgGroupIndex;
bbs.cursub = retObj.subBoardIndex;
continueChoosingMsgArea = false;
}
else
{
// A sub-board was not chosen, so we'll have to re-draw
// the header and list of message groups.
console.gotoxy(1, 1);
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();
console.gotoxy(1, 1);
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.
retObj = this.SelectSubBoard_Lightbar(bbs.curgrp);
// If the user chose a sub-board, then set bbs.cursub
if (retObj.subBoardChosen)
bbs.cursub = retObj.subBoardIndex;
}
}
// For the DDMsgAreaChooser class: Lets the user choose a sub-board within a
// message group, with a lightbar interface. Does not set bbs.cursub.
//
// Parameters:
// pGrpIndex: The index of the message group to choose from. This is
// optional; if not specified, bbs.curgrp 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.
function DDMsgAreaChooser_selectSubBoard_Lightbar(pGrpIndex, pMarkIndex)
{
// Create the return object.
var retObj = new Object();
retObj.subBoardChosen = false;
retObj.subBoardIndex = -1;
var grpIndex = 0;
if (typeof(pGrpIndex) == "number")
grpIndex = pGrpIndex;
else if ((bbs.curgrp != null) && (typeof(bbs.curgrp) == "number"))
grpIndex = bbs.curgrp;
// 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 ((bbs.cursub != null) && (typeof(bbs.cursub) == "number") && (bbs.curgrp == pGrpIndex))
markIndex = bbs.cursub;
// 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;
}
// Figure out the index of the user's currently-selected sub-board.
var selectedSubIndex = 0;
if ((bbs.cursub != null) && (typeof(bbs.cursub) == "number"))
{
if ((bbs.curgrp != null) && (typeof(bbs.curgrp) == "number") && (bbs.curgrp == pGrpIndex))
selectedSubIndex = bbs.cursub;
}
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
var listStartRow = 3+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 topSubIndex = 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 bottomSubIndex = getBottommostSubIndex(topSubIndex, numItemsPerPage);
// Figure out how many pages are needed to list all the sub-boards.
var numPages = Math.ceil(msg_area.grp_list[grpIndex].sub_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 (selectedSubIndex > bottomSubIndex)
{
var nextPageTopIndex = 0;
while (selectedSubIndex > bottomSubIndex)
{
nextPageTopIndex = topSubIndex + numItemsPerPage;
if (nextPageTopIndex < msg_area.grp_list[grpIndex].sub_list.length)
{
// Adjust topSubIndex and bottomSubIndex, and
// refresh the list on the screen.
topSubIndex = nextPageTopIndex;
bottomSubIndex = getBottommostSubIndex(topSubIndex, numItemsPerPage);
}
else
break;
}
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// 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 = ((topSubIndex < msg_area.grp_list[grpIndex].sub_list.length) &&
(selectedSubIndex >= topSubIndex) && (selectedSubIndex <= bottomSubIndex));
if (!foundCorrectPage)
{
topSubIndex = 0;
bottomSubIndex = getBottommostSubIndex(topSubIndex, numItemsPerPage);
selectedSubIndex = 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); // Don't need to since it should already be drawn
if (this.areaChangeHdrLines.length > 0)
console.crlf();
var pageNum = calcPageNum(topSubIndex, numItemsPerPage);
this.WriteSubBrdListHdr1Line(grpIndex, numPages, pageNum);
this.WriteKeyHelpLine();
var curpos = new Object();
curpos.x = 1;
curpos.y = 2+this.areaChangeHdrLines.length;
console.gotoxy(curpos);
printf(this.subBoardListHdrPrintfStr, "Sub #", "Name", "# Posts", "Latest date & time");
this.ListScreenfulOfSubBrds(grpIndex, topSubIndex, 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 continueChoosingSubBrd = true;
while (continueChoosingSubBrd)
{
// Highlight the currently-selected message group
highlightScrenRow = listStartRow + (selectedSubIndex - topSubIndex);
curpos.y = highlightScrenRow;
if ((highlightScrenRow > 0) && (highlightScrenRow < console.screen_rows))
{
console.gotoxy(1, highlightScrenRow);
this.WriteMsgSubBoardLine(grpIndex, selectedSubIndex, true);
}
// Get a key from the user (upper-case) and take action based upon it.
userInput = getKeyWithESCChars(K_UPPER | K_NOCRLF);
switch (userInput)
{
case KEY_UP: // Move up one message group in the list
if (selectedSubIndex > 0)
{
// If the previous group index is on the previous page, then
// display the previous page.
var previousSubIndex = selectedSubIndex - 1;
if (previousSubIndex < topSubIndex)
{
// Adjust topSubIndex and bottomSubIndex, and
// refresh the list on the screen.
topSubIndex -= numItemsPerPage;
bottomSubIndex = getBottommostSubIndex(topSubIndex, numItemsPerPage);
pageNum = calcPageNum(topSubIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, false, false);
this.ListScreenfulOfSubBrds(grpIndex, topSubIndex, listStartRow, listEndRow, false, true);
}
else
{
// Display the current line un-highlighted.
console.gotoxy(1, curpos.y);
this.WriteMsgSubBoardLine(grpIndex, selectedSubIndex, false);
}
selectedSubIndex = previousSubIndex;
}
break;
case KEY_DOWN: // Move down one message group in the list
if (selectedSubIndex < msg_area.grp_list[grpIndex].sub_list.length - 1)
{
// If the next group index is on the next page, then display
// the next page.
var nextGrpIndex = selectedSubIndex + 1;
if (nextGrpIndex > bottomSubIndex)
{
// Adjust topSubIndex and bottomSubIndex, and
// refresh the list on the screen.
topSubIndex += numItemsPerPage;
bottomSubIndex = getBottommostSubIndex(topSubIndex, numItemsPerPage);
pageNum = calcPageNum(topSubIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, false, false);
this.ListScreenfulOfSubBrds(grpIndex, topSubIndex, listStartRow, listEndRow, false, true);
}
else
{
// Display the current line un-highlighted.
console.gotoxy(1, curpos.y);
this.WriteMsgSubBoardLine(grpIndex, selectedSubIndex, false);
}
selectedSubIndex = nextGrpIndex;
}
break;
case KEY_HOME: // Go to the top message group on the screen
if (selectedSubIndex > topSubIndex)
{
// Display the current line un-highlighted, then adjust
// selectedSubIndex.
console.gotoxy(1, curpos.y);
this.WriteMsgSubBoardLine(grpIndex, selectedSubIndex, false);
selectedSubIndex = topSubIndex;
// 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 (selectedSubIndex < bottomSubIndex)
{
// Display the current line un-highlighted, then adjust
// selectedSubIndex.
console.gotoxy(1, curpos.y);
this.WriteMsgSubBoardLine(grpIndex, selectedSubIndex, false);
selectedSubIndex = bottomSubIndex;
// Note: curpos.y is set at the start of the while loop.
}
break;
case KEY_ENTER: // Select the currently-highlighted sub-board; and we're done.
continueChoosingSubBrd = false;
retObj.subBoardChosen = true;
retObj.subBoardIndex = selectedSubIndex;
break;
case KEY_PAGE_DOWN: // Go to the next page
var nextPageTopIndex = topSubIndex + numItemsPerPage;
if (nextPageTopIndex < msg_area.grp_list[grpIndex].sub_list.length)
{
// Adjust topSubIndex and bottomSubIndex, and
// refresh the list on the screen.
topSubIndex = nextPageTopIndex;
pageNum = calcPageNum(topSubIndex, numItemsPerPage);
bottomSubIndex = getBottommostSubIndex(topSubIndex, numItemsPerPage);
this.updatePageNumInHeader(pageNum, numPages, false, false);
this.ListScreenfulOfSubBrds(grpIndex, topSubIndex, listStartRow, listEndRow, false, true);
selectedSubIndex = topSubIndex;
}
break;
case KEY_PAGE_UP: // Go to the previous page
var prevPageTopIndex = topSubIndex - numItemsPerPage;
if (prevPageTopIndex >= 0)
{
// Adjust topSubIndex and bottomSubIndex, and
// refresh the list on the screen.
topSubIndex = prevPageTopIndex;
pageNum = calcPageNum(topSubIndex, numItemsPerPage);