Newer
Older
/* This is a message reader/lister door for Synchronet. Features include:
* - Listing messages in the user's current message area with the ability to
* navigate forwards & backwards through the list (and for ANSI users, a
* lightbar interface will be used, or optionally can be set to use a more
* traditional interface for ANSI users)
* - The user can select a message from the list to read and optionally reply to
* - For ANSI users, reading messages is done with an enhanced user interface,
* with the ability to scroll up & down through the message, move to the next
* or previous message using the right & left arrow keys, display the message
* list to choose another message to read, etc.
* - The ability to start up with the message list or reading messages in the
* user's current message area (AKA sub-board)
* - Message searching
*
* Author: Eric Oulashin (AKA Nightfox)
* BBS: Digital Distortion
* BBS address: digitaldistortionbbs.com (or digdist.bbsindex.com)
*
* Date Author Description
* 2014-09-13 Eric Oulashin Started (based on my message lister script)
* 2015-05-06 Eric Oulashin Version 1.0
* Finally releasing it, as it seems fairly stable
* and has the basic features implemented.
* 2015-05-17 Eric Oulashin Version 1.01
* Bug fix: Updated the setting of the enhanced reader
* header width to use the longest line in the header
* (rather than the length of only the first line) to

nightfox
committed
* ensure that the header displays correctly.
* 2015-06-10 Eric Oulashin Version 1.02
* Updated the version to reflect a bug fix in
* DDScanMsgs.js. No change to the actual reader.
* 2015-07-11 Eric Oulashin Version 1.03 Beta
* Started looking into & fixing an issue in Linux
* where after replying to a message, the number of
* messages was not immediately refreshed, so for
* instance, replying to the last message in read
* mode, the reader would not be able to navigate
* to the next message without first going to the
* previous message.
* 2015-07-12 Eric Oulashin Version 1.03
* Releasing this version after having done more testing.
* 2015-07-19 Eric Oulashin Version 1.04 Beta
* Updated to pause (wait for user keypress) after saving
* a reply message to allow the user to see Synchronet's
* success/fail message on saving a message.
* 2015-08-09 Eric Oulashin Adding the ability for the sysop to save a message
* to a file on the BBS machine
* 2015-09-07 Eric Oulashin Updated so that in lightbar mode, pressing PageDown
* on the last page will go to the last message, and
* pressing PageUp on the first page will go to the
* first message. Also, in the enhanced reader mode,
* added a console pause after posting a message in
* the sub-board so that the user can see the info
* screen that Synchronet displays after saving a
* message.
* 2015-09-19 Eric Oulashin Started working on adding the ability to download file
* attachments. Started working on a new function,
* determineMsgAttachments(), which can parse message text
* to save any base64-encoded attachments that might be
* present, and also to check the message subject for a
* filename for a file uploaded to the user's inbox.
* 2015-10-10 Eric Oulashin Version 1.04
* Releasing this version after development & testing,
* since attachment downloading and the other new features
* seem to be working fairly well.
* 2015-10-25 Eric Oulashin Version 1.05 Beta
* Started updating the reader to display more header &
* kludge lines.
* 2015-10-28 Eric Oulashin Started working on updating the ANSIAttrsToSyncAttrs()
* function to use Synchronet's ans2asc tool to convert
* from ANSI to Synchronet codes, to get ANSI messsages
* to look better.
* 2015-11-07 Eric Oulashin Expanded the list of @-codes interpreted for message
* headers. Also, renamed that method to ParseMsgAtCodes().
* Updated the ReadMessageEnhanced method to interpret
* @-codes, but only when reading personal mail, to avoid
* weird behavior on message networks from malicious users
* on other BBSes.
* 2015-11-24 Eric Oulashin Started working on using the Frame class (in frame.js)
* to display messages with ANSI codes using a scrollable
* user interface
* 2015-12-06 Eric Oulashin Version 1.05
* Officially releasing this version, as it seems to be
* fairly stable after testing.

nightfox
committed
* 2015-12-10 Eric Oulashin Version 1.06 beta
* Bug fix: The scriptFilename command-line argument was
* not being referenced properly in the DigDistMsgReader
* constructor; that has been fixed.

nightfox
committed
* 2015-12-11 Eric Oulashin Updated DigDistMsgReader_MessageAreaScan() so that
* the current sub-board newscan functionality can make
* use of the -subBoard command-line option to scan a
* specific sub-board, which may be different than the
* user's current sub-board.
* Bug fix: Updated DigDistMsgReader_MessageAreaScan()
* to temporarily set bbs.curgrp and bbs.cursub so that
* all @-codes for the sub-boards will be displayed
* correctly by Synchronet.
* 2015-12-12 Eric Oulashin Added a new configuration options, pauseAfterNewMsgScan,
* which specifies whether or not to pause after doing
* a new message scan.
* 2015-12-13 Eric Oulashin Version 1.06
* Releasing this version after testing showed it's
* working as expected
*/
/* Command-line arguments (in -arg=val format, or -arg format to enable an
option):
-search: A search type. Available options:
keyword_search: Do a keyword search in message subject/body text (current message area)
from_name_search: 'From' name search (current message area)
to_name_search: 'To' name search (current message area)
to_user_search: To user search (current message area)
new_msg_scan: New message scan (prompt for current sub-board, current
group, or all)
new_msg_scan_all: New message scan (all sub-boards)
new_msg_scan_cur_grp: New message scan (current message group only)

nightfox
committed
new_msg_scan_cur_sub: New message scan (current sub-board only). This
can (optionally) be used with the -subBoard
command-line parameter, which specifies an internal
code for a sub-board, which may be different from
the user's currently selected sub-board.
to_user_new_scan: Scan for new (unread) messages to the user (prompt
for current sub-board, current group, or all)
to_user_new_scan_all: Scan for new (unread) messages to the user
(all sub-boards)
to_user_new_scan_cur_grp: Scan for new (unread) messages to the user
(current group)
to_user_new_scan_cur_sub: Scan for new (unread) messages to the user
(current sub-board)
to_user_all_scan: Scan for all messages to the user (prompt for current
sub-board, current group, or all)
prompt: Prompt the user for one of several search/scan options to
choose from
Note that if the -personalEmail option is specified (to read personal
email), the only valid search types are keyword_search and
from_name_search.
-suppressSearchTypeText: Disable the search type text that would appear
above searches or scans (such as "New To You
Message Scan", etc.)
-startMode: Startup mode. Available options:
list (or lister): Message list mode
read (or reader): Message read mode
-configFilename: Specifies the name of the configuration file to use.
Defaults to DDMsgReader.cfg.
-personalEmail: Read personal email to the user. This is a true/false value.
It doesn't need to explicitly have a =true or =false afterward;
simply including -personalEmail will enable it. If this is specified,
the -chooseAreaFirst and -subBoard options will be ignored.
-personalEmailSent: Read personal email to the user. This is a true/false
value. It doesn't need to explicitly have a =true or =false
afterward; simply including -personalEmailSent will enable it.
-allPersonalEmail: Read all personal email (to/from all)
-userNum: Specify a user number (for the personal email options)
-chooseAreaFirst: Display the message area chooser before reading/listing
messages. This is a true/false value. It doesn't need
to explicitly have a =true or =false afterward; simply
including -chooseAreaFirst will enable it. If -personalEmail
or -subBoard is specified, then this option won't have any
effect.
-subBoard: The sub-board (internal code or number) to read, other than the user's
current sub-board. If this is specified, the -chooseAreaFirst option
will be ignored.
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
-verboseLogging: Enable logging to the system log & node log. Currently, there
isn't much that will be logged, but more log messages could be
added in the future.
*/
// TODO:
// - Idea for future release: Add some extra functionality to the enhanced
// reader interface (perhaps accessible on their own small menu or via
// CTRL hotkeys):
// - Forward the current message (to username, user #, internet email address,
// FTN email address, QWK email address, etc.)
// - Save the message to the BBS computer (sysop only)
load("sbbsdefs.js");
load("text.js"); // Text string definitions (referencing text.dat)
// This script requires Synchronet version 3.15 or higher.
// Exit if the Synchronet version is below the minimum.
if (system.version_num < 31500)
{
var message = "\1n\1h\1y\1i* Warning:\1n\1h\1w Digital Distortion Message Reader "
+ "requires version \1g3.15\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();
}
// Reader version information
var READER_VERSION = "1.06";
var READER_DATE = "2015-12-13";
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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
var LEFT_ARROW = ascii(17);
var RIGHT_ARROW = ascii(16);
// PageUp & PageDown keys - Not real key codes, but codes defined
// to be used & recognized in this script
var KEY_PAGE_UP = "\1PgUp";
var KEY_PAGE_DOWN = "\1PgDn";
// Ctrl keys for input
var CTRL_A = "\x01";
var CTRL_B = "\x02";
var CTRL_C = "\x03";
var CTRL_D = "\x04";
var CTRL_E = "\x05";
var CTRL_F = "\x06";
var CTRL_G = "\x07";
var BEEP = CTRL_G;
var CTRL_H = "\x08";
var BACKSPACE = CTRL_H;
var CTRL_I = "\x09";
var TAB = CTRL_I;
var CTRL_J = "\x0a";
var CTRL_K = "\x0b";
var CTRL_L = "\x0c";
var CTRL_M = "\x0d";
var CTRL_N = "\x0e";
var CTRL_O = "\x0f";
var CTRL_P = "\x10";
var CTRL_Q = "\x11";
var XOFF = CTRL_Q;
var CTRL_R = "\x12";
var CTRL_S = "\x13";
var XON = CTRL_S;
var CTRL_T = "\x14";
var CTRL_U = "\x15";
var CTRL_V = "\x16";
var KEY_INSERT = CTRL_V;
var CTRL_W = "\x17";
var CTRL_X = "\x18";
var CTRL_Y = "\x19";
var CTRL_Z = "\x1a";
//var KEY_ESC = "\x1b";
var KEY_ESC = ascii(27);
var KEY_ENTER = CTRL_M;
// These are defined in sbbsdefs.js:
//var KEY_UP ='\x1e'; // ctrl-^ (up arrow)
//var KEY_DOWN ='\x0a'; // ctrl-j (dn arrow)
//var KEY_RIGHT ='\x06'; // ctrl-f (rt arrow)
//var KEY_LEFT ='\x1d'; // ctrl-] (lf arrow)
//var KEY_HOME ='\x02'; // ctrl-b (home)
//var KEY_END ='\x05'; // ctrl-e (end)
//var KEY_DEL ='\x7f'; // (del)
// Characters for display
// Box-drawing/border characters: Single-line
var UPPER_LEFT_SINGLE = "Ú";
var HORIZONTAL_SINGLE = "Ä";
var UPPER_RIGHT_SINGLE = "¿";
var VERTICAL_SINGLE = "³";
var LOWER_LEFT_SINGLE = "À";
var LOWER_RIGHT_SINGLE = "Ù";
var T_SINGLE = "Â";
var LEFT_T_SINGLE = "Ã";
var RIGHT_T_SINGLE = "´";
var BOTTOM_T_SINGLE = "Á";
var CROSS_SINGLE = "Å";
// Box-drawing/border characters: Double-line
var UPPER_LEFT_DOUBLE = "É";
var HORIZONTAL_DOUBLE = "Í";
var UPPER_RIGHT_DOUBLE = "»";
var VERTICAL_DOUBLE = "º";
var LOWER_LEFT_DOUBLE = "È";
var LOWER_RIGHT_DOUBLE = "¼";
var T_DOUBLE = "Ë";
var LEFT_T_DOUBLE = "Ì";
var RIGHT_T_DOUBLE = "¹";
var BOTTOM_T_DOUBLE = "Ê";
var CROSS_DOUBLE = "Î";
// Box-drawing/border characters: Vertical single-line with horizontal double-line
var UPPER_LEFT_VSINGLE_HDOUBLE = "Õ";
var UPPER_RIGHT_VSINGLE_HDOUBLE = "¸";
var LOWER_LEFT_VSINGLE_HDOUBLE = "Ô";
var LOWER_RIGHT_VSINGLE_HDOUBLE = "¾";
// Other special characters
var DOT_CHAR = "ú";
var CHECK_CHAR = "û";
var THIN_RECTANGLE_LEFT = "Ý";
var THIN_RECTANGLE_RIGHT = "Þ";
var BLOCK1 = "°"; // Dimmest block
var BLOCK2 = "±";
var BLOCK3 = "²";
var BLOCK4 = "Û"; // Brightest block
const ERROR_PAUSE_WAIT_MS = 1500;
// gIsSysop stores whether or not the user is a sysop.
var gIsSysop = user.compare_ars("SYSOP"); // Whether or not the user is a sysop
// Store whether or not the Synchronet compile date is at least May 12, 2013
// so that we don't have to call compileDateAtLeast2013_05_12() multiple times.
var gSyncCompileDateAtLeast2013_05_12 = compileDateAtLeast2013_05_12();
// Reader mode definitions:
const READER_MODE_LIST = 0;
const READER_MODE_READ = 1;
// Search types
const SEARCH_NONE = -1;
const SEARCH_KEYWORD = 2;
const SEARCH_FROM_NAME = 3;
const SEARCH_TO_NAME_CUR_MSG_AREA = 4;
const SEARCH_TO_USER_CUR_MSG_AREA = 5;
const SEARCH_MSG_NEWSCAN = 6;
const SEARCH_MSG_NEWSCAN_CUR_SUB = 7;
const SEARCH_MSG_NEWSCAN_CUR_GRP = 8;
const SEARCH_MSG_NEWSCAN_ALL = 9;
const SEARCH_TO_USER_NEW_SCAN = 10;
const SEARCH_TO_USER_NEW_SCAN_CUR_SUB = 11;
const SEARCH_TO_USER_NEW_SCAN_CUR_GRP = 12;
const SEARCH_TO_USER_NEW_SCAN_ALL = 13;
const SEARCH_ALL_TO_USER_SCAN = 14;
// Message threading types

nightfox
committed
const THREAD_BY_ID = 15;
const THREAD_BY_TITLE = 16;
const THREAD_BY_AUTHOR = 17;
const THREAD_BY_TO_USER = 18;
// Reader mode - Actions

nightfox
committed
const ACTION_NONE = 19;
const ACTION_GO_NEXT_MSG = 20;
const ACTION_GO_PREVIOUS_MSG = 21;
const ACTION_GO_SPECIFIC_MSG = 22;
const ACTION_GO_FIRST_MSG = 23;
const ACTION_GO_LAST_MSG = 24;
const ACTION_DISPLAY_MSG_LIST = 25;
const ACTION_CHG_MSG_AREA = 26;
const ACTION_GO_PREV_MSG_AREA = 27;
const ACTION_GO_NEXT_MSG_AREA = 28;
const ACTION_QUIT = 29;
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
// Strings for the various message attributes (used by makeAllAttrStr(),
// makeMainMsgAttrStr(), makeAuxMsgAttrStr(), and makeNetMsgAttrStr())
var gMainMsgAttrStrs = new Object();
gMainMsgAttrStrs[MSG_DELETE] = "Del";
gMainMsgAttrStrs[MSG_PRIVATE] = "Priv";
gMainMsgAttrStrs[MSG_READ] = "Read";
gMainMsgAttrStrs[MSG_PERMANENT] = "Perm";
gMainMsgAttrStrs[MSG_LOCKED] = "Lock";
gMainMsgAttrStrs[MSG_ANONYMOUS] = "Anon";
gMainMsgAttrStrs[MSG_KILLREAD] = "Killread";
gMainMsgAttrStrs[MSG_MODERATED] = "Mod";
gMainMsgAttrStrs[MSG_VALIDATED] = "Valid";
gMainMsgAttrStrs[MSG_REPLIED] = "Repl";
gMainMsgAttrStrs[MSG_NOREPLY] = "NoRepl";
var gAuxMsgAttrStrs = new Object();
gAuxMsgAttrStrs[MSG_FILEREQUEST] = "Freq";
gAuxMsgAttrStrs[MSG_FILEATTACH] = "Attach";
gAuxMsgAttrStrs[MSG_TRUNCFILE] = "TruncFile";
gAuxMsgAttrStrs[MSG_KILLFILE] = "KillFile";
gAuxMsgAttrStrs[MSG_RECEIPTREQ] = "RctReq";
gAuxMsgAttrStrs[MSG_CONFIRMREQ] = "ConfReq";
gAuxMsgAttrStrs[MSG_NODISP] = "NoDisp";
var gNetMsgAttrStrs = new Object();
gNetMsgAttrStrs[MSG_LOCAL] = "FromLocal";
gNetMsgAttrStrs[MSG_INTRANSIT] = "Transit";
gNetMsgAttrStrs[MSG_SENT] = "Sent";
gNetMsgAttrStrs[MSG_KILLSENT] = "KillSent";
gNetMsgAttrStrs[MSG_ARCHIVESENT] = "ArcSent";
gNetMsgAttrStrs[MSG_HOLD] = "Hold";
gNetMsgAttrStrs[MSG_CRASH] = "Crash";
gNetMsgAttrStrs[MSG_IMMEDIATE] = "Now";
gNetMsgAttrStrs[MSG_DIRECT] = "Direct";
gNetMsgAttrStrs[MSG_GATE] = "Gate";
gNetMsgAttrStrs[MSG_ORPHAN] = "Orphan";
gNetMsgAttrStrs[MSG_FPU] = "FPU";
gNetMsgAttrStrs[MSG_TYPELOCAL] = "ForLocal";
gNetMsgAttrStrs[MSG_TYPEECHO] = "ForEcho";
gNetMsgAttrStrs[MSG_TYPENET] = "ForNetmail";
// 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(/[\/\\][^\/\\]*$/,''));
// See if we're running in Windows or not. Until early 2015, the word_wrap()
// function seemed to have a bug where the wrapping length in Linux was one
// less than what it uses in Windows). That seemed to be fixed in one of the
// Synchronet 3.16 builds in early 2015.
var gRunningInWindows = /^WIN/.test(system.platform.toUpperCase());
// Temporary directory (in the logged-in user's node directory) to store
// file attachments, etc.
var gFileAttachDir = backslash(system.node_dir + "DDMsgReader_Attachments");
// If the temporary attachments directory exists, then delete it (in case the last
// user hung up while running this script, etc.)
if (file_exists(gFileAttachDir))
deltree(gFileAttachDir);
// See if frame.js and scrollbar.js exist in sbbs/exec/load on the BBS machine.
// If so, load them. They will be used for displaying messages with ANSI content
// with a scrollable user interface.
var gFrameJSAvailable = file_exists(backslash(system.exec_dir) + "load/frame.js");
if (gFrameJSAvailable)
load("frame.js");
var gScrollbarJSAvailable = file_exists(backslash(system.exec_dir) + "load/scrollbar.js");
if (gScrollbarJSAvailable)
load("scrollbar.js");
/////////////////////////////////////////////
// Script execution code
// Parse the command-line arguments
var gCmdLineArgVals = parseArgs(argv);
var gAllPersonalEmailOptSpecified = (gCmdLineArgVals.hasOwnProperty("allpersonalemail") && gCmdLineArgVals.allpersonalemail);
// Check to see if the command-line argument for reading personal email is enabled
var gListPersonalEmailCmdLineOpt = ((gCmdLineArgVals.hasOwnProperty("personalemail") && gCmdLineArgVals.personalemail) ||
(gCmdLineArgVals.hasOwnProperty("personalemailsent") && gCmdLineArgVals.personalemailsent) ||
gAllPersonalEmailOptSpecified);
// If the command-line parameter "search" is specified as "prompt", then
// prompt the user for the type of search to perform.

nightfox
committed
var gDoDDMR = true; // If the user doesn't choose a search type, this will be set to false
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
if (gCmdLineArgVals.hasOwnProperty("search") && (gCmdLineArgVals["search"].toLowerCase() == "prompt"))
{
console.print("\1n");
console.crlf();
console.print("\1cMessage search:");
console.crlf();
var allowedKeys = "";
if (!gListPersonalEmailCmdLineOpt)
{
allowedKeys = "ANKFTYUS";
console.print(" \1g\1hN\1y = \1n\1cNew message scan");
console.crlf();
console.print(" \1g\1hK\1y = \1n\1cKeyword");
console.crlf();
console.print(" \1h\1gF\1y = \1n\1cFrom name");
console.crlf();
console.print(" \1h\1gT\1y = \1n\1cTo name");
console.crlf();
console.print(" \1h\1gY\1y = \1n\1cTo you");
console.crlf();
console.print(" \1h\1gU\1y = \1n\1cUnread (new) messages to you");
console.crlf();
console.print(" \1h\1gS\1y = \1n\1cScan for msgs to you");
console.crlf();
}
else
{
// Reading personal email - Allow fewer choices
allowedKeys = "KF";
console.print(" \1g\1hK\1y = \1n\1cKeyword");
console.crlf();
console.print(" \1h\1gF\1y = \1n\1cFrom name");
console.crlf();
}
console.print(" \1h\1gA\1y = \1n\1cAbort");
console.crlf();
console.print("\1n\1cMake a selection\1g\1h: \1c");
// TODO: Check to see if keyword & from name search work when reading
// personal email
switch (console.getkeys(allowedKeys))
{
case "N":
gCmdLineArgVals["search"] = "new_msg_scan";
break;
case "K":
gCmdLineArgVals["search"] = "keyword_search";
break;
case "F":
gCmdLineArgVals["search"] = "from_name_search";
break;
case "T":
gCmdLineArgVals["search"] = "to_name_search";
break;
case "Y":
gCmdLineArgVals["search"] = "to_user_search";
break;
case "U":
gCmdLineArgVals["search"] = "to_user_new_scan";
break;
case "S":
gCmdLineArgVals["search"] = "to_user_all_scan";
break;
case "A": // Abort
default:

nightfox
committed
gDoDDMR = false;
console.print("\1n\1h\1y\1iAborted\1n");
console.crlf();
console.pause();
break;
}
}

nightfox
committed
if (gDoDDMR)
{
// Create an instance of the DigDistMsgReader class and use it to read/list the
// messages in the user's current sub-board. Pass the parsed command-line
// argument values object to its constructor.
var readerSubCode = (gListPersonalEmailCmdLineOpt ? "mail" : bbs.cursub_code);
// If the -subBoard option was specified and the "read personal email" option was
// not specified, then use the sub-board specified by the -subBoard command-line
// option.
if (gCmdLineArgVals.hasOwnProperty("subboard") && !gListPersonalEmailCmdLineOpt)
{
// If the specified sub-board option is all digits, then treat it as the
// sub-board number. Otherwise, treat it as an internal sub-board code.
if (/^[0-9]+$/.test(gCmdLineArgVals["subboard"]))
readerSubCode = getSubBoardCodeFromNum(Number(gCmdLineArgVals["subboard"]));
else
readerSubCode = gCmdLineArgVals["subboard"];
}
var msgReader = new DigDistMsgReader(readerSubCode, gCmdLineArgVals);
// If the option to choose a message area first was enabled on the command-line
// (and neither the -subBoard nor the -personalEmail options were specified),
// then let the user choose a sub-board now.
if (gCmdLineArgVals.hasOwnProperty("chooseareafirst") && gCmdLineArgVals["chooseareafirst"] && !gCmdLineArgVals.hasOwnProperty("subboard") && !gListPersonalEmailCmdLineOpt)
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
msgReader.SelectMsgArea();
// Back up the user's current sub-board so that we can change back
// to it after searching is done, if a search is done.
var originalMsgGrpIdx = bbs.curgrp;
var originalSubBoardIdx = bbs.cursub;
var restoreOriginalSubCode = true;
// Based on the reader's start mode/search type, do the appropriate thing.
switch (msgReader.searchType)
{
case SEARCH_NONE:
restoreOriginalSubCode = false;
msgReader.ReadOrListSubBoard();
break;
case SEARCH_KEYWORD:
msgReader.SearchMessages("keyword_search");
break;
case SEARCH_FROM_NAME:
msgReader.SearchMessages("from_name_search");
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
msgReader.SearchMessages("to_name_search");
break;
case SEARCH_TO_USER_CUR_MSG_AREA:
msgReader.SearchMessages("to_user_search");
break;
case SEARCH_MSG_NEWSCAN:
if (!gCmdLineArgVals.suppresssearchtypetext)
{
console.crlf();
console.print(msgReader.text.newMsgScanText);
console.crlf();
}
msgReader.MessageAreaScan(SCAN_CFG_NEW, SCAN_NEW);
break;
case SEARCH_MSG_NEWSCAN_CUR_SUB:
msgReader.MessageAreaScan(SCAN_CFG_NEW, SCAN_NEW, "S");
break;
case SEARCH_MSG_NEWSCAN_CUR_GRP:
msgReader.MessageAreaScan(SCAN_CFG_NEW, SCAN_NEW, "G");
break;
case SEARCH_MSG_NEWSCAN_ALL:
msgReader.MessageAreaScan(SCAN_CFG_NEW, SCAN_NEW, "A");
break;
case SEARCH_TO_USER_NEW_SCAN:
if (!gCmdLineArgVals.suppresssearchtypetext)
{
console.crlf();
console.print(msgReader.text.newToYouMsgScanText);
console.crlf();
}
msgReader.MessageAreaScan(SCAN_CFG_TOYOU/*SCAN_CFG_YONLY*/, SCAN_UNREAD);
break;
case SEARCH_TO_USER_NEW_SCAN_CUR_SUB:
msgReader.MessageAreaScan(SCAN_CFG_TOYOU/*SCAN_CFG_YONLY*/, SCAN_UNREAD, "S");
break;
case SEARCH_TO_USER_NEW_SCAN_CUR_GRP:
msgReader.MessageAreaScan(SCAN_CFG_TOYOU/*SCAN_CFG_YONLY*/, SCAN_UNREAD, "G");
break;
case SEARCH_TO_USER_NEW_SCAN_ALL:
msgReader.MessageAreaScan(SCAN_CFG_TOYOU/*SCAN_CFG_YONLY*/, SCAN_UNREAD, "A");
break;
case SEARCH_ALL_TO_USER_SCAN:
if (!gCmdLineArgVals.suppresssearchtypetext)
{
console.crlf();
console.print(msgReader.text.allToYouMsgScanText);
console.crlf();
}
msgReader.MessageAreaScan(SCAN_CFG_TOYOU, SCAN_TOYOU);
break;
}
// If we should restore the user's original message area, then do so.
if (restoreOriginalSubCode)
{
bbs.cursub = 0;
bbs.curgrp = originalMsgGrpIdx;
bbs.cursub = originalSubBoardIdx;
}
// Remove the temporary attachments directory if it exists
deltree(gFileAttachDir);
// Before this script finishes, make sure the terminal attributes are set back
// to normal (in case there are any attributes left on, such as background,
// blink, etc.)
console.print("\1n");

nightfox
committed
if (console.term_supports(USER_ANSI))
console.print("[0m");
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
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
}
// End of script execution. Functions below:
///////////////////////////////////////////////////////////////////////////////////
// DigDistMsgReader class stuff
// DigDistMsgReader class constructor: Constructs a
// DigDistMsgReader object, to be used for listing messages
// in a message area.
//
// Parameters:
// pSubBoardCode: Optional - The Synchronet sub-board code, or "mail"
// for personal email.
// pScriptArgs: Optional - An object containing key/value pairs representing
// the command-line arguments & values, as returned by parseArgs().
function DigDistMsgReader(pSubBoardCode, pScriptArgs)
{
// startMode specifies the mode for the reader to start in - List mode
// or reader mode, etc. This is a setting that is read from the configuration
// file. The configuration file can be either READER_MODE_READ or
// READER_MODE_LIST, but the optional "mode" parameter in pArgv can specify
// another mode.
this.startMode = READER_MODE_LIST;
// msgSearchHdrs is an object containing message headers found via searching.
// It is indexed by internal message area code. Each internal code index
// will specify an object containing the following properties:
// indexed: A standard 0-based array containing message headers
this.msgSearchHdrs = new Object();
this.searchString = ""; // To be used for message searching
// this.searchType will specify the type of search:
// SEARCH_NONE (-1): No search
// SEARCH_KEYWORD: Keyword search in message subject & body
// SEARCH_FROM_NAME: Search by 'from' name
// SEARCH_TO_NAME_CUR_MSG_AREA: Search by 'to' name
// SEARCH_TO_USER_CUR_MSG_AREA: Search by 'to' name, to the current user
// SEARCH_MSG_NEWSCAN: New (unread) message scan (prompt the user for sub, group, or all)
// SEARCH_MSG_NEWSCAN_CUR_SUB: New (unread) message scan (current sub-board)
// SEARCH_MSG_NEWSCAN_CUR_GRP: New (unread) message scan (current message group)
// SEARCH_MSG_NEWSCAN_ALL: New (unread) message scan (all message sub-boards)
// SEARCH_TO_USER_NEW_SCAN: New (unread) messages to the current user (prompt the user for sub, group, or all)
// SEARCH_TO_USER_NEW_SCAN_CUR_SUB: New (unread) messages to the current user (current sub-board)
// SEARCH_TO_USER_NEW_SCAN_CUR_GRP: New (unread) messages to the current user (current group)
// SEARCH_TO_USER_NEW_SCAN_ALL: New (unread) messages to the current user (all sub-board)
// SEARCH_ALL_TO_USER_SCAN: All messages to the current user
this.searchType = SEARCH_NONE;
this.subBoardCode = bbs.cursub_code; // The message sub-board code
this.readingPersonalEmail = false;
// A method to set subBoardCode and readingPersonalEmail
this.setSubBoardCode = DigDistMsgReader_SetSubBoardCode;
// this.colors will be an array of colors to use in the message list
this.colors = getDefaultColors();
this.msgbase = null; // Will be a MsgBase object.
this.readingPersonalEmailFromUser = false;

nightfox
committed
if (typeof(pSubBoardCode) == "string")
{

nightfox
committed
var subCodeLowerCase = pSubBoardCode.toLowerCase();
if (subBoardCodeIsValid(subCodeLowerCase))
{
this.setSubBoardCode(subCodeLowerCase);
this.msgbase = new MsgBase(this.subBoardCode);
if (gCmdLineArgVals.hasOwnProperty("personalemailsent") && gCmdLineArgVals.personalemailsent)
this.readingPersonalEmailFromUser = true;
}
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
}
// This property controls whether or not the user will be prompted to
// continue listing messages after selecting a message to read.
this.promptToContinueListingMessages = false;
// Whether or not to prompt the user to confirm to read a message
this.promptToReadMessage = false;
// String lengths for the columns to write
// Fixed field widths: Message number, date, and time
this.MSGNUM_LEN = 4;
this.DATE_LEN = 10; // i.e., YYYY-MM-DD
this.TIME_LEN = 8; // i.e., HH:MM:SS
// Variable field widths: From, to, and subject (based on a screen width of
// 80 columns)
this.FROM_LEN = (console.screen_columns * (15/80)).toFixed(0);
this.TO_LEN = (console.screen_columns * (15/80)).toFixed(0);
this.SUBJ_LEN = (console.screen_columns * (22/80)).toFixed(0);
// Whether or not the user chose to read a message
this.readAMessage = false;
// Whether or not the user denied confirmation to read a message
this.deniedReadingMessage = false;
// msgListUseLightbarListInterface specifies whether or not to use the lightbar
// interface for the message list. The lightbar interface will only be used if
// the user's terminal supports ANSI.
this.msgListUseLightbarListInterface = true;
// Whether or not to use the scrolling interface when reading a message
// (will only be used for ANSI terminals).
this.scrollingReaderInterface = true;
// reverseListOrder stores whether or not to arrange the message list descending
// by date.
this.reverseListOrder = false;
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
741
742
743
744
745
746
747
748
749
750
751
// displayBoardInfoInHeader specifies whether or not to display
// the message group and sub-board lines in the header at the
// top of the screen (an additional 2 lines).
this.displayBoardInfoInHeader = false;
// msgList_displayMessageDateImported specifies whether or not to use the
// message import date as the date displayed in the message list. If false,
// the message written date will be displayed.
this.msgList_displayMessageDateImported = true;
// The number of spaces to use for tab characters - Used in the
// extended read mode
this.numTabSpaces = 3;
// Construct the header format string
this.sHdrFormatStr = "%" + this.MSGNUM_LEN + "s %-" + this.FROM_LEN + "s %-"
+ this.TO_LEN + "s %-" + this.SUBJ_LEN + "s %-"
+ this.DATE_LEN + "s %-" + this.TIME_LEN + "s";
// If the user's terminal doesn't support ANSI, then append a newline to
// the end of the format string (we won't be able to move the cursor).
if (!canDoHighASCIIAndANSI())
this.sHdrFormatStr += "\r\n";
// this.text is an object containing text used for various functionality.
this.text = new Object();
this.text.scrollbarBGChar = BLOCK1;
this.text.scrollbarScrollBlockChar = BLOCK2;
this.text.goToPrevMsgAreaPromptText = "\1n\1c\1hGo to the previous message area";
this.text.goToNextMsgAreaPromptText = "\1n\1c\1hGo to the next message area";
this.text.newMsgScanText = "\1c\1hN\1n\1cew \1hM\1n\1cessage \1hS\1n\1ccan";
this.text.newToYouMsgScanText = "\1c\1hN\1n\1cew \1hT\1n\1co \1hY\1n\1cou \1hM\1n\1cessage \1hS\1n\1ccan";
this.text.allToYouMsgScanText = "\1c\1hA\1n\1cll \1hM\1n\1cessages \1hT\1n\1co \1hY\1n\1cou \1hS\1n\1ccan";
this.text.scanScopePromptText = "\1n\1h\1wS\1n\1gub-board, \1h\1wG\1n\1group, or \1h\1wA\1n\1gll \1h(\1wENTER\1n\1g to cancel\1h)\1n\1g: \1h\1c";
this.text.goToMsgNumPromptText = "\1n\1cGo to message # (or \1hENTER\1n\1c to cancel)\1g\1h: \1c";
this.text.msgScanAbortedText = "\1n\1h\1cM\1n\1cessage scan \1h\1y\1iaborted\1n";
this.text.deleteMsgNumPromptText = "\1n\1cNumber of the message to be deleted (or \1hENTER\1n\1c to cancel)\1g\1h: \1c";
this.text.editMsgNumPromptText = "\1n\1cNumber of the message to be edited (or \1hENTER\1n\1c to cancel)\1g\1h: \1c";
this.text.searchingSubBoardAbovePromptText = "\1n\1cSearching (current sub-board: \1b\1h%s\1n\1c)";
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
this.text.searchingSubBoardText = "\1n\1cSearching \1h%s\1n\1c...";
this.text.noMessagesInSubBoardText = "\1n\1h\1bThere are no messages in the area \1w%s\1b.";
this.text.noSearchResultsInSubBoardText = "\1n\1h\1bNo messages were found in the area \1w%s\1b with the given search criteria.";
this.text.msgScanCompleteText = "\1n\1h\1cM\1n\1cessage scan complete\1h\1g.\1n";
this.text.invalidMsgNumText = "\1n\1y\1hInvalid message number: %d";
this.text.readMsgNumPromptText = "\1n\1g\1h\1i* \1n\1cRead message #: \1h";
this.text.msgHasBeenDeletedText = "\1n\1h\1g* \1yMessage #\1w%d \1yhas been deleted.";
this.text.noKludgeLinesForThisMsgText = "\1n\1h\1yThere are no kludge lines for this message.";
this.text.searchingPersonalMailText = "\1w\1hSearching personal mail\1n";
this.text.searchTextPromptText = "\1cEnter the search text\1g\1h:\1n\1c ";
this.text.fromNamePromptText = "\1cEnter the 'from' name to search for\1g\1h:\1n\1c ";
this.text.toNamePromptText = "\1cEnter the 'to' name to search for\1g\1h:\1n\1c ";
this.text.abortedText = "\1n\1y\1h\1iAborted\1n";
this.text.loadingPersonalMailText = "\1n\1cLoading %s...";
this.text.msgDelConfirmText = "\1n\1h\1yDelete\1n\1c message #\1h%d\1n\1c: Are you sure";
this.text.msgDeletedText = "\1n\1cMessage #\1h%d\1n\1c has been marked for deletion.";
this.text.cannotDeleteMsgText_notYoursNotASysop = "\1n\1h\1wCannot delete message #\1y%d \1wbecause it's not yours or you're not a sysop.";
this.text.cannotDeleteMsgText_notLastPostedMsg = "\1n\1h\1g* \1yCannot delete message #%d. You can only delete your last message in this area.\1n";
this.text.msgEditConfirmText = "\1n\1cEdit message #\1h%d\1n\1c: Are you sure";
this.text.noPersonalEmailText = "\1n\1cYou have no messages.";
// Set the methods for the object
this.RefreshSearchResultMsgHdr = DigDistMsgReader_RefreshSearchResultMsgHdr; // Refreshes a message header in the search results
this.SearchMessages = DigDistMsgReader_SearchMessages; // Prompts the user for search text, then lists/reads messages, performing the search
this.ReadMessages = DigDistMsgReader_ReadMessages;
this.DisplayEnhancedMsgReadHelpLine = DigDistMsgReader_DisplayEnhancedMsgReadHelpLine;
this.GoToPrevSubBoardForEnhReader = DigDistMsgReader_GoToPrevSubBoardForEnhReader;
this.GoToNextSubBoardForEnhReader = DigDistMsgReader_GoToNextSubBoardForEnhReader;

nightfox
committed
this.SetUpTraditionalMsgListVars = DigDistMsgReader_SetUpTraditionalMsgListVars;
this.SetUpLightbarMsgListVars = DigDistMsgReader_SetUpLightbarMsgListVars;
this.ListMessages = DigDistMsgReader_ListMessages;
this.ListMessages_Traditional = DigDistMsgReader_ListMessages_Traditional;
this.ListMessages_Lightbar = DigDistMsgReader_ListMessages_Lightbar;
this.ClearSearchData = DigDistMsgReader_ClearSearchData;
this.ReadOrListSubBoard = DigDistMsgReader_ReadOrListSubBoard;
this.PopulateHdrsIfSearch_DispErrorIfNoMsgs = DigDistMsgReader_PopulateHdrsIfSearch_DispErrorIfNoMsgs;
this.SearchTypePopulatesSearchResults = DigDistMsgReader_SearchTypePopulatesSearchResults;
this.SearchTypeRequiresSearchText = DigDistMsgReader_SearchTypeRequiresSearchText;
this.MessageAreaScan = DigDistMsgReader_MessageAreaScan;
this.PromptContinueOrReadMsg = DigDistMsgReader_PromptContinueOrReadMsg;

nightfox
committed
this.WriteMsgListScreenTopHeader = DigDistMsgReader_WriteMsgListScreenTopHeader;
this.ReadMessage = DigDistMsgReader_ReadMessage;
this.ReadMessageEnhanced = DigDistMsgReader_ReadMessageEnhanced;
this.EnhReaderPrepLast2LinesForPrompt = DigDistMsgReader_EnhReaderPrepLast2LinesForPrompt;
this.LookForNextOrPriorNonDeletedMsg = DigDistMsgReader_LookForNextOrPriorNonDeletedMsg;
this.PrintMessageInfo = DigDistMsgReader_PrintMessageInfo;
this.ListScreenfulOfMessages = DigDistMsgReader_ListScreenfulOfMessages;

nightfox
committed
this.DisplayMsgListHelp = DigDistMsgReader_DisplayMsgListHelp;
this.DisplayTraditionalMsgListHelp = DigDistMsgReader_DisplayTraditionalMsgListHelp;
this.DisplayLightbarMsgListHelp = DigDistMsgReader_DisplayLightbarMsgListHelp;
this.DisplayMessageListNotesHelp = DigDistMsgReader_DisplayMessageListNotesHelp;
this.SetMsgListPauseTextAndLightbarHelpLine = DigDistMsgReader_SetMsgListPauseTextAndLightbarHelpLine;
this.SetEnhancedReaderHelpLine = DigDistMsgReader_SetEnhancedReaderHelpLine;
this.EditExistingMsg = DigDistMsgReader_EditExistingMsg;
this.CanDelete = DigDistMsgReader_CanDelete;
this.CanDeleteLastMsg = DigDistMsgReader_CanDeleteLastMsg;
this.CanEdit = DigDistMsgReader_CanEdit;
this.CanQuote = DigDistMsgReader_CanQuote;
this.ReadConfigFile = DigDistMsgReader_ReadConfigFile;
this.DisplaySyncMsgHeader = DigDistMsgReader_DisplaySyncMsgHeader;
this.GetMsgHdrFilenameFull = DigDistMsgReader_GetMsgHdrFilenameFull;
this.GetMsgHdrByIdx = DigDistMsgReader_GetMsgHdrByIdx;
this.GetMsgHdrByMsgNum = DigDistMsgReader_GetMsgHdrByMsgNum;
this.GetMsgHdrByAbsoluteNum = DigDistMsgReader_GetMsgHdrByAbsoluteNum;
this.AbsMsgNumToIdx = DigDistMsgReader_AbsMsgNumToIdx;
this.IdxToAbsMsgNum = DigDistMsgReader_IdxToAbsMsgNum;
this.NumMessages = DigDistMsgReader_NumMessages;
this.HighestMessageNum = DigDistMsgReader_HighestMessageNum;
this.IsValidMessageNum = DigDistMsgReader_IsValidMessageNum;
this.PromptForMsgNum = DigDistMsgReader_PromptForMsgNum;
this.ParseMsgAtCodes = DigDistMsgReader_ParseMsgAtCodes;
this.ReplaceMsgAtCodeFormatStr = DigDistMsgReader_ReplaceMsgAtCodeFormatStr;
this.FindNextNonDeletedMsgIdx = DigDistMsgReader_FindNextNonDeletedMsgIdx;

nightfox
committed
this.ChangeSubBoard = DigDistMsgReader_ChangeSubBoard;
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
this.EnhancedReaderChangeSubBoard = DigDistMsgReader_EnhancedReaderChangeSubBoard;
this.ReplyToMsg = DigDistMsgReader_ReplyToMsg;
this.DoPrivateReply = DigDistMsgReader_DoPrivateReply;
this.DisplayEnhancedReaderHelp = DigDistMsgReader_DisplayEnhancedReaderHelp;
this.DisplayEnhancedMsgHdr = DigDistMsgReader_DisplayEnhancedMsgHdr;
this.DisplayEnhancedReaderWholeScrollbar = DigDistMsgReader_DisplayEnhancedReaderWholeScrollbar;
this.UpdateEnhancedReaderScollbar = DigDistMsgReader_UpdateEnhancedReaderScollbar;
this.MessageIsDeleted = DigDistMsgReader_MessageIsDeleted;
this.MessageIsLastFromUser = DigDistMsgReader_MessageIsLastFromUser;
this.DisplayEnhReaderError = DigDistMsgReader_DisplayEnhReaderError;
this.EnhReaderPromptYesNo = DigDistMsgReader_EnhReaderPromptYesNo;
this.DeleteMessage = DigDistMsgReader_DeleteMessage;
this.GetExtdMsgHdrInfo = DigDistMsgReader_GetExtdMsgHdrInfo;
this.GetMsgInfoForEnhancedReader = DigDistMsgReader_GetMsgInfoForEnhancedReader;
this.GetLastReadMsgIdx = DigDistMsgReader_GetLastReadMsgIdx;
this.GetScanPtrMsgIdx = DigDistMsgReader_GetScanPtrMsgIdx;
this.SearchingAndResultObjsDefinedForCurSub = DigDistMsgReader_SearchingAndResultObjsDefinedForCurSub;
this.RemoveFromSearchResults = DigDistMsgReader_RemoveFromSearchResults;
this.FindThreadNextOffset = DigDistMsgReader_FindThreadNextOffset;
this.FindThreadPrevOffset = DigDistMsgReader_FindThreadPrevOffset;
// These two variables keep track of whether we're doing a message scan that spans
// multiple sub-boards so that the enhanced reader function can enable use of
// the > key to go to the next sub-board.
this.doingMultiSubBoardScan = false;
// An option for using the scrollable interface for messages with ANSI
// content - The sysop can set this to false if the sysop thinks the
// scrolling ANSI interface (using frame.js and scrollbar.js) doesn't
// look good enough
this.useScrollingInterfaceForANSIMessages = true;
// Whether or not to pause (with a message) after doing a new message scan
this.pauseAfterNewMsgScan = true;
this.cfgFilename = "DDMsgReader.cfg";
// Check the command-line arguments for a custom configuration file name
// before reading the configuration file.
var scriptArgsIsValid = (typeof(pScriptArgs) == "object");
if (scriptArgsIsValid && pScriptArgs.hasOwnProperty("configfilename"))
this.cfgFilename = pScriptArgs["configfilename"];
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
1001
1002
1003
1004
1005
1006
// Read the settings from the config file
this.cfgFileSuccessfullyRead = false;
this.ReadConfigFile();
// Set any other values specified by the command-line parameters
// Reader start mode - Read or list mode
if (scriptArgsIsValid)
{
if (pScriptArgs.hasOwnProperty("startmode"))
{
var readerStartMode = readerModeStrToVal(pScriptArgs["startmode"]);
if (readerStartMode != -1)
this.startMode = readerStartMode;
}
// Search mode
if (pScriptArgs.hasOwnProperty("search"))
{
var searchType = searchTypeStrToVal(pScriptArgs["search"]);
if (searchType != SEARCH_NONE)
this.searchType = searchType;
}
}
// Color value adjusting (must be done after reading the config file in case
// the color settings were changed from defaults)
// Message list highlight colors: For each (except for the background),
// prepend the normal attribute and append the background attribute to the end.
// This is to ensure that high attributes don't affect the rest of the line and
// the background attribute stays for the rest of the line.
this.colors.msgListMsgNumHighlightColor = "\1n" + this.colors.msgListMsgNumHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListFromHighlightColor = "\1n" + this.colors.msgListFromHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListToHighlightColor = "\1n" + this.colors.msgListToHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListSubjHighlightColor = "\1n" + this.colors.msgListSubjHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListDateHighlightColor = "\1n" + this.colors.msgListDateHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListTimeHighlightColor = "\1n" + this.colors.msgListTimeHighlightColor + this.colors.msgListHighlightBkgColor;
// Similar for the area chooser lightbar highlight colors
this.colors.areaChooserMsgAreaNumHighlightColor = "\1n" + this.colors.areaChooserMsgAreaNumHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaDescHighlightColor = "\1n" + this.colors.areaChooserMsgAreaDescHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaDateHighlightColor = "\1n" + this.colors.areaChooserMsgAreaDateHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaTimeHighlightColor = "\1n" + this.colors.areaChooserMsgAreaTimeHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaNumItemsHighlightColor = "\1n" + this.colors.areaChooserMsgAreaNumItemsHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
// Similar for the enhanced reader help line colors
this.colors.enhReaderHelpLineGeneralColor = "\1n" + this.colors.enhReaderHelpLineGeneralColor + this.colors.enhReaderHelpLineBkgColor;
this.colors.enhReaderHelpLineHotkeyColor = "\1n" + this.colors.enhReaderHelpLineHotkeyColor + this.colors.enhReaderHelpLineBkgColor;
this.colors.enhReaderHelpLineParenColor = "\1n" + this.colors.enhReaderHelpLineParenColor + this.colors.enhReaderHelpLineBkgColor;
// Similar for the lightbar message list help line colors
this.colors.lightbarMsgListHelpLineGeneralColor = "\1n" + this.colors.lightbarMsgListHelpLineGeneralColor + this.colors.lightbarMsgListHelpLineBkgColor;
this.colors.lightbarMsgListHelpLineHotkeyColor = "\1n" + this.colors.lightbarMsgListHelpLineHotkeyColor + this.colors.lightbarMsgListHelpLineBkgColor;
this.colors.lightbarMsgListHelpLineParenColor = "\1n" + this.colors.lightbarMsgListHelpLineParenColor + this.colors.lightbarMsgListHelpLineBkgColor;
// Similar for the lightbar area chooser help line colors
this.colors.lightbarAreaChooserHelpLineGeneralColor = "\1n" + this.colors.lightbarAreaChooserHelpLineGeneralColor + this.colors.lightbarAreaChooserHelpLineBkgColor;
this.colors.lightbarAreaChooserHelpLineHotkeyColor = "\1n" + this.colors.lightbarAreaChooserHelpLineHotkeyColor + this.colors.lightbarAreaChooserHelpLineBkgColor;
this.colors.lightbarAreaChooserHelpLineParenColor = "\1n" + this.colors.lightbarAreaChooserHelpLineParenColor + this.colors.lightbarAreaChooserHelpLineBkgColor;
// Prepend most of the text strings with the normal attribute (if they don't
// have it already) to make sure the correct colors are used.
for (var prop in this.text)
{
if ((prop != "scrollbarBGChar") && (prop != "scrollbarScrollBlockChar"))
{
if ((this.text[prop].length > 0) && (this.text[prop].charAt(0) != "\1n"))
this.text[prop] = "\1n" + this.text[prop];
}
}
// this.tabReplacementText will be the text that tabs will be replaced
// with in enhanced reader mode
this.tabReplacementText = format("%" + this.numTabSpaces + "s", "");
// Construct the message information format string. These must be done after
// reading the configuration file, because the configuration file specifies the
// colors to use.
this.sMsgInfoFormatStr = this.colors.msgListMsgNumColor + "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListFromColor + "%-" + this.FROM_LEN + "s "
+ this.colors.msgListToColor + "%-" + this.TO_LEN + "s "
+ this.colors.msgListSubjectColor + "%-" + this.SUBJ_LEN + "s "
+ this.colors.msgListDateColor + "%-" + this.DATE_LEN + "s "
+ this.colors.msgListTimeColor + "%-" + this.TIME_LEN + "s";
// Message information format string with colors to use when the message is
// written to the user.
this.sMsgInfoToUserFormatStr = this.colors.msgListToUserMsgNumColor + "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListToUserFromColor
+ "%-" + this.FROM_LEN + "s " + this.colors.msgListToUserToColor + "%-"
+ this.TO_LEN + "s " + this.colors.msgListToUserSubjectColor + "%-"
+ this.SUBJ_LEN + "s " + this.colors.msgListToUserDateColor
+ "%-" + this.DATE_LEN + "s " + this.colors.msgListToUserTimeColor
+ "%-" + this.TIME_LEN + "s";
// Message information format string with colors to use when the message is
// from the user.
this.sMsgInfoFromUserFormatStr = this.colors.msgListFromUserMsgNumColor + "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListFromUserFromColor
+ "%-" + this.FROM_LEN + "s " + this.colors.msgListFromUserToColor + "%-"
+ this.TO_LEN + "s " + this.colors.msgListFromUserSubjectColor + "%-"
+ this.SUBJ_LEN + "s " + this.colors.msgListFromUserDateColor
+ "%-" + this.DATE_LEN + "s " + this.colors.msgListFromUserTimeColor
+ "%-" + this.TIME_LEN + "s";
// Highlighted message information line for the message list (used for the
// lightbar interface)
this.sMsgInfoFormatHighlightStr = this.colors.msgListMsgNumHighlightColor
+ "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListFromHighlightColor + "%-" + this.FROM_LEN
+ "s " + this.colors.msgListToHighlightColor + "%-" + this.TO_LEN + "s "
+ this.colors.msgListSubjHighlightColor + "%-" + this.SUBJ_LEN + "s "
+ this.colors.msgListDateHighlightColor + "%-" + this.DATE_LEN + "s "
+ this.colors.msgListTimeHighlightColor + "%-" + this.TIME_LEN + "s";
// If the user's terminal doesn't support ANSI, then append a newline to
// the end of the format string (we won't be able to move the cursor).
if (!canDoHighASCIIAndANSI())
{
this.sMsgInfoFormatStr += "\r\n";
this.sMsgInfoToUserFormatStr += "\r\n";
this.sMsgInfoFromUserFormatStr += "\r\n";
this.sMsgInfoFormatHighlightStr += "\r\n";
}
// Enhanced reader help line (will be set up in
// DigDistMsgReader_SetEnhancedReaderHelpLine())
this.enhReadHelpLine = "";
// Read the enhanced message header file and populate this.enhMsgHeaderLines,
// the header text for enhanced reader mode. The enhanced reader header file
// name will start with 'enhMsgHeader', and there can be multiple versions for
// different terminal widths (i.e., msgHeader_80.ans for an 80-column console
// and msgHeader_132 for a 132-column console).
this.enhMsgHeaderLines = new Array();
var enhHsgHdrFileExists = true;
var enhMsgHdrFilenameBase = "enhMsgHeader";
var enhMsgHdrFilenameBaseFullPath = gStartupPath + enhMsgHdrFilenameBase;
// See if there is a header file that is made for the user's terminal
// width (msgHeader-<width>.ans/asc). If not, then just go with
// msgHeader.ans/asc.
var enhMsgHdrFilename = "";
if (file_exists(enhMsgHdrFilenameBaseFullPath + "-" + console.screen_columns + ".ans"))
enhMsgHdrFilename = enhMsgHdrFilenameBaseFullPath + "-" + console.screen_columns + ".ans";
else if (file_exists(enhMsgHdrFilenameBaseFullPath + "-" + console.screen_columns + ".asc"))
enhMsgHdrFilename = enhMsgHdrFilenameBaseFullPath + "-" + console.screen_columns + ".asc";
else if (file_exists(enhMsgHdrFilenameBaseFullPath + ".ans"))
enhMsgHdrFilename = enhMsgHdrFilenameBaseFullPath + ".ans";
else if (file_exists(enhMsgHdrFilenameBaseFullPath + ".asc"))
enhMsgHdrFilename = enhMsgHdrFilenameBaseFullPath + ".asc";
else
{
// The enhanced reader header file doesn't exist, so provide some default
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
// header lines.
enhHsgHdrFileExists = false;
// Group name: 20% of console width
// Sub-board name: 34% of console width
var msgGrpNameLen = Math.floor(console.screen_columns * 0.2);
var subBoardNameLen = Math.floor(console.screen_columns * 0.34);
var hdrLine1 = "\1n\1h\1c" + UPPER_LEFT_SINGLE + HORIZONTAL_SINGLE + "\1n\1c"
+ HORIZONTAL_SINGLE + " \1h@GRP-L";
var numChars = msgGrpNameLen - 7;
for (var i = 0; i < numChars; ++i)
hdrLine1 += "#";
hdrLine1 += "@ @SUB-L";
numChars = subBoardNameLen - 7;
for (var i = 0; i < numChars; ++i)
hdrLine1 += "#";
hdrLine1 += "@\1k";
numChars = console.screen_columns - console.strlen(hdrLine1) - 4;
for (var i = 0; i < numChars; ++i)
hdrLine1 += HORIZONTAL_SINGLE;
hdrLine1 += "\1n\1c" + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\1h"
+ HORIZONTAL_SINGLE + UPPER_RIGHT_SINGLE;
this.enhMsgHeaderLines.push(hdrLine1);
var hdrLine2 = "\1n\1c" + VERTICAL_SINGLE + "\1h\1k" + BLOCK1 + BLOCK2
+ BLOCK3 + "\1gM\1n\1gsg#\1h\1c: \1b@MSG_NUM_AND_TOTAL-L";
numChars = console.screen_columns - 32;
for (var i = 0; i < numChars; ++i)
hdrLine2 += "#";
hdrLine2 += "@\1n\1c" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine2);
var hdrLine3 = "\1n\1h\1k" + VERTICAL_SINGLE + BLOCK1 + BLOCK2 + BLOCK3
+ "\1gF\1n\1grom\1h\1c: \1b@MSG_FROM-L";
numChars = console.screen_columns - 23;
for (var i = 0; i < numChars; ++i)
hdrLine3 += "#";
hdrLine3 += "@\1k" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine3);
var hdrLine4 = "\1n\1h\1k" + VERTICAL_SINGLE + BLOCK1 + BLOCK2 + BLOCK3
+ "\1gT\1n\1go \1h\1c: \1b@MSG_TO-L";
numChars = console.screen_columns - 21;
for (var i = 0; i < numChars; ++i)
hdrLine4 += "#";
hdrLine4 += "@\1k" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine4);
var hdrLine5 = "\1n\1h\1k" + VERTICAL_SINGLE + BLOCK1 + BLOCK2 + BLOCK3
+ "\1gS\1n\1gubj\1h\1c: \1b@MSG_SUBJECT-L";
numChars = console.screen_columns - 26;
for (var i = 0; i < numChars; ++i)
hdrLine5 += "#";
hdrLine5 += "@\1k" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine5);
var hdrLine6 = "\1n\1c" + VERTICAL_SINGLE + "\1h\1k" + BLOCK1 + BLOCK2 + BLOCK3
+ "\1gD\1n\1gate\1h\1c: \1b@MSG_DATE-L";
numChars = console.screen_columns - 23;
for (var i = 0; i < numChars; ++i)
hdrLine6 += "#";
hdrLine6 += "@\1n\1c" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine6);
var hdrLine7 = "\1n\1h\1c" + BOTTOM_T_SINGLE + HORIZONTAL_SINGLE + "\1n\1c"
+ HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\1h\1k";
numChars = console.screen_columns - 8;
for (var i = 0; i < numChars; ++i)
hdrLine7 += HORIZONTAL_SINGLE;
hdrLine7 += "\1c" + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\1h"
+ HORIZONTAL_SINGLE + BOTTOM_T_SINGLE;
this.enhMsgHeaderLines.push(hdrLine7);
}
if (enhHsgHdrFileExists)
{
// If the header file is ANSI, then convert it to Synchronet attribute
// codes and read that file instead. This is done so that this script can
// accurately get the file line lengths using console.strlen().
var syncConvertedHdrFilename = enhMsgHdrFilenameBaseFullPath + "_converted.asc";
if (!file_exists(syncConvertedHdrFilename))
{
var dotIdx = enhMsgHdrFilename.lastIndexOf(".");
if (dotIdx > -1)
{
// If header file is ANSI, then convert it to Synchronet attribute
// format and save it as an .asc file. Otherwise, just use the
// header file without conversion since it's already ASCII or
// Synchronet attribute code format.
var isANSI = (enhMsgHdrFilename.substr(dotIdx+1).toUpperCase() == "ANS");
if (isANSI)
{
var filenameBase = enhMsgHdrFilename.substr(0, dotIdx);
var cmdLine = system.exec_dir + "ans2asc \"" + enhMsgHdrFilename + "\" \""
+ syncConvertedHdrFilename + "\"";
// Note: Both system.exec(cmdLine) and
// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
// execute the command, but system.exec() seems noticeably faster.
system.exec(cmdLine);
}
else
syncConvertedHdrFilename = enhMsgHdrFilename;
}
}
// Read the header file into this.enhMsgHeaderLines
var hdrFile = new File(syncConvertedHdrFilename);
if (hdrFile.open("r"))
{
var fileLine = null;
while (!hdrFile.eof && (this.enhMsgHeaderLines.length <= 10))
{
// Read the next line from the header file.
fileLine = hdrFile.readln(2048);
// fileLine should be a string, but I've seen some cases
// where it isn't, so check its type.
if (typeof(fileLine) != "string")
continue;
// Make sure the line isn't longer than the user's terminal
//if (fileLine.length > console.screen_columns)
// fileLine = fileLine.substr(0, console.screen_columns);
this.enhMsgHeaderLines.push(fileLine);
}
}
}
// Save the enhanced reader header width. This will be the length of the longest
// line in the header.
this.enhMsgHeaderWidth = 0;
if (this.enhMsgHeaderLines.length > 0)
{
var lineLen = 0;
for (var i = 0; i < this.enhMsgHeaderLines.length; ++i)
{
lineLen = console.strlen(this.enhMsgHeaderLines[i]);
if (lineLen > this.enhMsgHeaderWidth)
this.enhMsgHeaderWidth = lineLen;
}
}
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
// Message display area information
this.msgAreaTop = this.enhMsgHeaderLines.length + 1;
this.msgAreaBottom = console.screen_rows-1; // The last line of the message area
// msgAreaLeft and msgAreaRight are the rightmost and leftmost columns of the
// message area, respectively. These are 1-based. 1 is subtracted from
// msgAreaRight to leave room for the scrollbar in enhanced reader mode.
this.msgAreaLeft = 1;
this.msgAreaRight = console.screen_columns - 1;
this.msgAreaWidth = this.msgAreaRight - this.msgAreaLeft + 1;
this.msgAreaHeight = this.msgAreaBottom - this.msgAreaTop + 1;
//////////////////////////////////////////////
// Things related to changing to a different message group & sub-board
// In the message area lists (for changing to another message area), the
// date & time of the last-imported message will be shown.
// msgAreaList_lastImportedMsg_showImportTime is a boolean to specify
// whether or not to use the import time for the last-imported message.
// If false, the message written time will be used.
this.msgAreaList_lastImportedMsg_showImportTime = 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;
// Some methods for choosing the message area
this.WriteChgMsgAreaKeysHelpLine = DigDistMsgReader_WriteLightbarChgMsgAreaKeysHelpLine;
this.WriteGrpListHdrLine = DigDistMsgReader_WriteGrpListTopHdrLine;
this.WriteSubBrdListHdr1Line = DMsgAreaChooser_WriteSubBrdListHdr1Line;
this.SelectMsgArea = DigDistMsgReader_SelectMsgArea;
this.SelectMsgArea_Lightbar = DigDistMsgReader_SelectMsgArea_Lightbar;
this.SelectSubBoard_Lightbar = DigDistMsgReader_SelectSubBoard_Lightbar;
this.SelectMsgArea_Traditional = DigDistMsgReader_SelectMsgArea_Traditional;
this.ListMsgGrps = DigDistMsgReader_ListMsgGrps_Traditional;
this.ListSubBoardsInMsgGroup = DigDistMsgReader_ListSubBoardsInMsgGroup_Traditional;
// Lightbar-specific methods
this.ListScreenfulOfMsgGrps = DigDistMsgReader_listScreenfulOfMsgGrps;
this.WriteMsgGroupLine = DigDistMsgReader_writeMsgGroupLine;
this.UpdateMsgAreaPageNumInHeader = DigDistMsgReader_updateMsgAreaPageNumInHeader;
this.ListScreenfulOfSubBrds = DigDistMsgReader_ListScreenfulOfSubBrds;
this.WriteMsgSubBoardLine = DigDistMsgReader_WriteMsgSubBrdLine;
// Choose Message Area help screen
this.ShowChooseMsgAreaHelpScreen = DigDistMsgReader_showChooseMsgAreaHelpScreen;
// Method to build the sub-board printf information for a message
// group
this.BuildSubBoardPrintfInfoForGrp = DigDistMsgReader_BuildSubBoardPrintfInfoForGrp;
// Methods for calculating a page number for a message list item
this.CalcTraditionalMsgListTopIdx = DigDistMsgReader_CalcTraditionalMsgListTopIdx;
this.CalcLightbarMsgListTopIdx = DigDistMsgReader_CalcLightbarMsgListTopIdx;
this.CalcMsgListScreenIdxVarsFromMsgNum = DigDistMsgReader_CalcMsgListScreenIdxVarsFromMsgNum;
// A method for validating a user's choice of message area
this.ValidateMsgAreaChoice = DigDistMsgReader_ValidateMsgAreaChoice;
this.SaveMsgToFile = DigDistMsgReader_SaveMsgToFile;
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
// printf strings for message group/sub-board lists
// Message group information (printf strings)
this.msgGrpListPrintfStr = "\1n " + this.colors.areaChooserMsgAreaNumColor + "%" + this.areaNumLen
+ "d " + this.colors.areaChooserMsgAreaDescColor + "%-"
+ this.msgGrpDescLen + "s " + this.colors.areaChooserMsgAreaNumItemsColor
+ "%" + this.numItemsLen + "d";
this.msgGrpListHilightPrintfStr = "\1n" + this.colors.areaChooserMsgAreaBkgHighlightColor + " "
+ "\1n" + this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaNumHighlightColor + "%" + this.areaNumLen
+ "d \1n" + this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaDescHighlightColor + "%-"
+ this.msgGrpDescLen + "s \1n" + this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaNumItemsHighlightColor + "%" + this.numItemsLen
+ "d";
// Message group list header (printf string)
this.msgGrpListHdrPrintfStr = this.colors.areaChooserMsgAreaHeaderColor + "%6s %-"
+ +(this.msgGrpDescLen-8) + "s %-12s";
// Sub-board information header (printf string)
this.subBoardListHdrPrintfStr = this.colors.areaChooserMsgAreaHeaderColor + " %5s %-"
+ +(this.subBoardNameLen-3) + "s %-7s %-19s";
// Lightbar area chooser help line text
// TODO: Account for wide terminals?
this.lightbarAreaChooserHelpLine = "\1n"
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + ""
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + ""
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "ENTER"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "HOME"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "END"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "#"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "N"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "ext pg, "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "P"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "rev pg, "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "F"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "irst pg, "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "L"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "ast pg, "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "Q"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "uit, "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "? ";
// 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();
// Variables to save the top message index for the traditional & lightbar
// message lists. Initialize them to -1 to mean the message list hasn't been
// displayed yet - In that case, the lister will use the user's last
// read pointer.
this.tradListTopMsgIdx = -1;
this.tradMsgListNumLines = console.screen_rows-3;
if (this.displayBoardInfoInHeader)
this.tradMsgListNumLines -= 2;
this.lightbarListTopMsgIdx = -1;
this.lightbarMsgListNumLines = console.screen_rows-2;
this.lightbarMsgListStartScreenRow = 2; // The first line number on the screen for the message list
// If we will be displaying the message group and sub-board in the
// header at the top of the screen (an additional 2 lines), then
// update this.lightbarMsgListNumLines and this.lightbarMsgListStartScreenRow to
// account for this.
if (this.displayBoardInfoInHeader)
{
this.lightbarMsgListNumLines -= 2;
this.lightbarMsgListStartScreenRow += 2;
}
// The selected message index for the lightbar message list (initially -1, will
// be set in the lightbar list method)
this.lightbarListSelectedMsgIdx = -1;
// The selected message cursor position for the lightbar message list (initially
// null, will be set in the lightbar list message)
this.lightbarListCurPos = null;
}
// For the DigDistMsgReader class: Sets the subBoardCode property and also
// sets the readingPersonalEmail property, a boolean for whether or not
// personal email is being read (whether the sub-board code is "mail")
//
// Parameters:
// pSubCode: The sub-board code to set in the object
function DigDistMsgReader_SetSubBoardCode(pSubCode)
{
this.subBoardCode = pSubCode;
this.readingPersonalEmail = (this.subBoardCode == "mail");
}
// Refreshes a message header in the message header arrays in this.msgSearchHdrs.
//
// Parameters:
// pMsgIndex: The index (0-based) of the message header
// pAttrib: Optional - An attribute to apply. If this is is not specified,
// then the message header will be retrieved from the message base.
function DigDistMsgReader_RefreshSearchResultMsgHdr(pMsgIndex, pAttrib)
{
if (typeof(pMsgIndex) != "number")
return;
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode))
{
var msgNum = pMsgIndex + 1;
if (typeof(pAttrib) != "undefined")
{
if (this.msgSearchHdrs[this.subBoardCode].indexed.hasOwnProperty(pMsgIndex))
{
this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex].attr = this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex].attr | pAttrib;
var msgOffsetFromHdr = this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex].offset;
this.msgbase.put_msg_header(true, msgOffsetFromHdr, this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex]);
}
}
else
{
var msgHeader = this.GetMsgHdrByIdx(pMsgIndex);
if (this.msgSearchHdrs[this.subBoardCode].indexed.hasOwnProperty(pMsgIndex))
{
this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex] = msgHeader;
this.msgbase.put_msg_header(true, msgHeader.offset, msgHeader);
}
}
}
}
// For the DigDistMsgReader class: Inputs search text from the user, then reads/lists
// messages, which will perform the search.
//
// Paramters:
// pSearchModeStr A string to specify the lister mode to use - This can
// be one of the search modes to specify how to search:
// "keyword_search": Search the message subjects & bodies by keyword
// "from_name_search": Search messages by from name
// "to_name_search": Search messages by to name
// "to_user_search": Search messages by to name, to the logged-in user
// pSubBoardCode: Optional - The Synchronet sub-board code, or "mail"
// for personal email.
function DigDistMsgReader_SearchMessages(pSearchModeStr, pSubBoardCode)
{
// Convert the search mode string to an integer representing the search
// mode. If we get back -1, that means the search mode string was invalid.
// If that's the case, simply list messages. Otherwise, do the search.
this.searchType = searchTypeStrToVal(pSearchModeStr);
if (this.searchType == SEARCH_NONE) // No search; search mode string was invalid
{
// Clear the search information and read/list messages.
this.ClearSearchData();
this.ReadOrListSubBoard(pSubBoardCode);
}
else
{
// The search mode string was valid, so go ahead and search.
console.print("\1n");
console.crlf();
var subCode = (typeof(pSubBoardCode) == "string" ? pSubBoardCode : this.subBoardCode);
if (subCode == "mail")
console.print("\1n" + this.text.searchingPersonalMailText);
else
console.print("\1n" + this.text.searchingSubBoardAbovePromptText.replace("%s", subBoardGrpAndName(bbs.cursub_code)) + "\1n");
console.crlf();
// Output the prompt text to the user (for modes where a prompt is needed)
switch (this.searchType)
{
case SEARCH_KEYWORD:
console.print("\1n" + this.text.searchTextPromptText);
break;
case SEARCH_FROM_NAME:
console.print("\1n" + this.text.fromNamePromptText);
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
console.print("\1n" + this.text.toNamePromptText);
break;
case SEARCH_TO_USER_CUR_MSG_AREA:
// Note: No prompt needed for this - Will search for the user's name/handle
console.line_counter = 0; // To prevent a pause before the message list comes up
break;
default:
break;
}
//var promptUserForText = this.SearchTypePopulatesSearchResults();
var promptUserForText = this.SearchTypeRequiresSearchText();
// Get the search text from the user
if (promptUserForText)
this.searchString = console.getstr(512, K_UPPER);
// If the user was prompted for search text but no search text was entered,
// then show an abort message and don't do anything. Otherwise, go ahead
// and list/read messages.
if (promptUserForText && (this.searchString.length == 0))
{
this.ClearSearchData();
console.print("\1n" + this.text.abortedText);
console.crlf();
console.pause();
return;
}
else
{
// List/read messages
this.ReadOrListSubBoard(pSubBoardCode);
// Clear the search data so that subsequent listing or reading sessions
// don't repeat the same search
this.ClearSearchData();
}
}
}
// This function clears the search data from the object.
function DigDistMsgReader_ClearSearchData()
{
this.searchType = SEARCH_NONE;
this.searchString == "";
if (this.msgSearchHdrs != null)
{
for (var subCode in this.msgSearchHdrs)
{
delete this.msgSearchHdrs[subCode].indexed;
delete this.msgSearchHdrs[subCode];
}
delete this.msgSearchHdrs;
this.msgSearchHdrs = new Object();
}
}
// For the DigDistMsgReader class: Performs message reading/listing.
// Depending on the value of this.startMode, starts in either reader
// mode or lister mode. Uses an input loop to let the user switch
// between the two modes.
//
// Parameters:
// pSubBoardCode: Optional - The internal code of a sub-board to read.
// If not specified, the internal sub-board code specified
// when creating the object will be used.
// pStartingMsgOffset: Optional - The offset of a message to start at
// pAllowChgArea: Optional boolean - Whether or not to allow changing the
// message area
// pReturnOnNextAreaNav: Optional boolean - Whether or not this method should
// return when it would move to the next message area due
// navigation from the user (i.e., with the right arrow key)
// pPauseOnNoMsgSrchResults: Optional boolean - Whether or not to pause when
// a message search doesn't find any search results
// in the current sub-board. Defaults to true.
//
// Return value: An object with the following properties:
// stoppedReading: Boolean - Whether or not the user stopped reading.
// This can also be true if there is an error.
function DigDistMsgReader_ReadOrListSubBoard(pSubBoardCode, pStartingMsgOffset,
pAllowChgArea, pReturnOnNextAreaNav,
pPauseOnNoMsgSrchResults)
{
var retObj = new Object();
retObj.stoppedReading = false;
// Set the sub-board code if applicable
var previousSubBoardCode = this.subBoardCode;
if (typeof(pSubBoardCode) == "string")
{
if (subBoardCodeIsValid(pSubBoardCode))
this.setSubBoardCode(pSubBoardCode);
else
{
console.print("\1n\1h\1yWarning: \1wThe Message Reader connot continue because an invalid");
console.crlf();
console.print("sub-board code was specified (" + pSubBoardCode + "). Please notify the sysop.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
}
// (re)-open the message base
if (previousSubBoardCode != this.subBoardCode)
{
if ((this.msgbase != null) && (this.msgbase.is_open))
this.msgbase.close();
this.msgbase = new MsgBase(this.subBoardCode);
}
else if (this.msgbase == null)
this.msgbase = new MsgBase(this.subBoardCode);
// Open the sub-board. If the message base was not opened, then output
// an error and return.
if (!this.msgbase.is_open && !this.msgbase.open())
{
console.print("\1n");
console.crlf();
console.print("\1h\1y* \1wUnable to open message sub-board:");
console.crlf();
console.print(subBoardGrpAndName(this.subBoardCode));
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
// Populate this.msgSearchHdrs for the current sub-board if there is a search
// specified. If there are no messages to read in the current sub-board, then
// just return.
var pauseOnNoSearchResults = (typeof(pPauseOnNoMsgSrchResults) == "boolean" ? pPauseOnNoMsgSrchResults : true);
if (!this.PopulateHdrsIfSearch_DispErrorIfNoMsgs(true, true, pauseOnNoSearchResults))
{
retObj.stoppedReading = false;
return retObj;
}
// Check the pAllowChgArea parameter. If it's a boolean, then use it. If
// not, then check to see if we're reading personal mail - If not, then allow
// the user to change to a different message area.
var allowChgMsgArea = true;
if (typeof(pAllowChgArea) == "boolean")
allowChgMsgArea = pAllowChgArea;
else
allowChgMsgArea = (this.subBoardCode != "mail");
// If reading personal email and messages haven't been collected (searched)
// yet, then do so now.
if (this.readingPersonalEmail && (!this.msgSearchHdrs.hasOwnProperty(this.subBoardCode)))
this.msgSearchHdrs[this.subBoardCode] = searchMsgbase(this.subBoardCode, this.msgbase, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
// Determine whether to start in list or reader mode, depending
// on the value of this.startMode.
var readerMode = this.startMode;
// User input loop
var selectedMessageOffset = 0;
if (typeof(pStartingMsgOffset) == "number")
selectedMessageOffset = pStartingMsgOffset;
else if (this.SearchingAndResultObjsDefinedForCurSub())
{
// If reading personal mail, start at the first unread message index
// (or the last message, if all messages have been read)
if (this.readingPersonalEmail)
{
selectedMessageOffset = this.GetLastReadMsgIdx(false); // Used to be true
if ((selectedMessageOffset > -1) && (selectedMessageOffset < this.NumMessages() - 1))
++selectedMessageOffset;
}
else
selectedMessageOffset = 0;
}
else
selectedMessageOffset = -1;
var otherRetObj = null;
var continueOn = true;
while (continueOn)
{
switch (readerMode)
{
case READER_MODE_READ:
// Call the ReadMessages method - DOn't change the sub-board,
// and pass the selected index of the message to read. If that
// index is -1, the ReadMessages method will use the user's
// last-read message index.
otherRetObj = this.ReadMessages(null, selectedMessageOffset, true,

nightfox
committed
allowChgMsgArea, pReturnOnNextAreaNav);
// If the user wants to quit or if there was an error, then stop
// the input loop.
if (otherRetObj.stoppedReading)
{
retObj.stoppedReading = true;
continueOn = false;
}
// If we're set to return on navigation to the next message area and
// the user's last keypress was the right arrow key or next action
// was to go to the next message area, then don't continue the input
// loop, and also say that the user didn't stop reading.
else if (pReturnOnNextAreaNav &&
((otherRetObj.lastUserInput == KEY_RIGHT) || (otherRetObj.lastUserInput == KEY_ENTER) || (otherRetObj.lastAction == ACTION_GO_NEXT_MSG_AREA)))
{
retObj.stoppedReading = false;
continueOn = false;
}
else if (otherRetObj.messageListReturn)
readerMode = READER_MODE_LIST;
break;
case READER_MODE_LIST:
// Note: Doing the message list is also handled in this.ReadMessages().
// This code is here in case the reader is configured to start up
// in list mode first.
// Call the ListMessages method - Don't change the sub-board, and
// have it return if the user chooses a message to read.

nightfox
committed
otherRetObj = this.ListMessages(null, true, pAllowChgArea);
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
// If the user wants to quit, set continueOn to false to get out
// of the loop. Otherwise, set the selected message offset to
// what the user chose from the list.
if (otherRetObj.lastUserInput == "Q")
{
retObj.stoppedReading = true;
continueOn = false;
}
else
{
selectedMessageOffset = otherRetObj.selectedMsgOffset;
readerMode = READER_MODE_READ;
}
break;
default:
break;
}
}
// Close the message base object (if it has not been closed already),
// re-enable the normal text attribute, and clear the screen.
if (this.msgbase != null)
{
this.msgbase.close();
this.msgbase = null;
}
console.clear("\1n");
return retObj;
}
// Helper for DigDistMsgReader_ReadOrListSubBoard(): Populates this.msgSearchHdrs
// if an applicable search type is specified; also, if there are no messages in
// the current sub-board, outputs an error to the user.
//
// Parameters:
// pCloseMsgbaseAndSetNullIfNoMsgs: Optional boolean - Whether or not to close the message
// base if there are no messages. Defaults to true.
// pOutputMessages: Boolean - Whether or not to output messages to the screen.
// Defaults to true.
// pPauseOnNoMsgError: Optional boolean - Whether or not to pause for a keypress
// after displaying the "no messages" error. Defaults to true.
//
// Return value: Boolean - Whether or not there are messages to read in the current
// sub-board
function DigDistMsgReader_PopulateHdrsIfSearch_DispErrorIfNoMsgs(pCloseMsgbaseAndSetNullIfNoMsgs,
pOutputMessages, pPauseOnNoMsgError)
{
var thereAreMessagesToRead = true;
var outputMessages = (typeof(pOutputMessages) == "boolean" ? pOutputMessages : true);
// If a search is is specified that would populate the search results, then
// perform the message search for the current sub-board.
if (this.SearchTypePopulatesSearchResults())
{
if (!this.msgSearchHdrs.hasOwnProperty(this.subBoardCode))
{
// TODO: In case new messages were posted in this sub-board, it might help
// to check the current number of messages vs. the previous number of messages
// and search the new messages if there are more.
if (outputMessages)
{
console.crlf();
if (this.readingPersonalEmail)
console.print("\1n" + this.text.loadingPersonalMailText.replace("%s", subBoardGrpAndName(this.subBoardCode)));
else
console.print(this.text.searchingSubBoardText.replace("%s", subBoardGrpAndName(this.subBoardCode)));
}
this.msgSearchHdrs[this.subBoardCode] = searchMsgbase(this.subBoardCode, this.msgbase, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
}
}
else
{
// There is no search is specified, so clear the search results for the
// current sub-board to help ensure that there are messages to read.
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode))
{
delete this.msgSearchHdrs[this.subBoardCode].indexed;
delete this.msgSearchHdrs[this.subBoardCode];
}
}
// If there are no messages to display in the current sub-board, then set the
// return value and let the user know (if outputMessages is true).
if (this.NumMessages() == 0)
{
thereAreMessagesToRead = false;
var closeMsgbase = (typeof(pCloseMsgbaseAndSetNullIfNoMsgs) == "boolean" ? pCloseMsgbaseAndSetNullIfNoMsgs : true);
if (closeMsgbase)
{
this.msgbase.close();
this.msgbase = null;
}
if (outputMessages)
{
console.print("\1n");
console.crlf();
if (this.readingPersonalEmail)
console.print(this.text.noPersonalEmailText);
else
{
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode))
console.print(this.text.noSearchResultsInSubBoardText.replace("%s", subBoardGrpAndName(this.subBoardCode)));
else
console.print(this.text.noMessagesInSubBoardText.replace("%s", subBoardGrpAndName(this.subBoardCode)));
}
console.crlf();
var pauseOnNoMsgsError = (typeof(pPauseOnNoMsgError) == "boolean" ? pPauseOnNoMsgError : true);
if (pauseOnNoMsgsError)
console.pause();
}
}
return thereAreMessagesToRead;
}
// For the DigDistMsgReader class: Returns whether the search type is a type
// that would result in the search results structure being populated. Search
// types where that wouldn't happen are SEARCH_NONE (no search) and any of the
// message scan search types.
function DigDistMsgReader_SearchTypePopulatesSearchResults()
{
return (this.readingPersonalEmail || searchTypePopulatesSearchResults(this.searchType));
}
// For the DigDistMsgReader class: Returns whether the search type is a type
// that requires search text. Search types that require search text are the
// keyword search, from name search, and to name search. Search types that
// don't require search text are SEARCH_NONE (no search) & the message scan search
// types.
function DigDistMsgReader_SearchTypeRequiresSearchText()
{
return searchTypeRequiresSearchText(this.searchType);
}
// Returns whether a search type value would populate search results.
//
// Parameters:
// pSearchType: A search type integer value
//
// Return value: Boolean - Whether or not the search type would populate search
// results
function searchTypePopulatesSearchResults(pSearchType)
{
return ((pSearchType == SEARCH_KEYWORD) ||
(pSearchType == SEARCH_FROM_NAME) ||
(pSearchType == SEARCH_TO_NAME_CUR_MSG_AREA) ||
(pSearchType == SEARCH_TO_USER_CUR_MSG_AREA) ||
(pSearchType == SEARCH_TO_USER_NEW_SCAN) ||
(pSearchType == SEARCH_TO_USER_NEW_SCAN_CUR_SUB) ||
(pSearchType == SEARCH_TO_USER_NEW_SCAN_CUR_GRP) ||
(pSearchType == SEARCH_TO_USER_NEW_SCAN_ALL) ||
(pSearchType == SEARCH_ALL_TO_USER_SCAN));
}
// Returns whether a search type value requires search text.
//
// Parameters:
// pSearchType: A search type integer value
//
// Return value: Boolean - Whether or not the search type requires search text
function searchTypeRequiresSearchText(pSearchType)
{
return ((pSearchType == SEARCH_KEYWORD) ||
(pSearchType == SEARCH_FROM_NAME) ||
(pSearchType == SEARCH_TO_NAME_CUR_MSG_AREA));
}
// For the DigDistMsgReader class: Scans the message area(s) for new messages,
// unread messages to the user, or all messages to the user.
//
// Parameters:
// pScanCfgOpt: The scan configuration option to check for in the sub-boards
// (from sbbsdefs.js). Supported values are SCAN_CFG_NEW (new
// message scan) and SCAN_CFG_TOYOU (messages to the user).
// pScanMode: The scan mode (from sbbsdefs.js). Supported values are SCAN_NEW
// (new message scan), SCAN_TOYOU (scan for all messages to the
// user), and SCAN_UNREAD (scan for new messages to the user).
// pScanScopeChar: Optional - A character (as a string) representing the scan
// scope: "S" for sub-board, "G" for group, or "A" for all.
// If this is not specified, the user will be prompted for the
// scan scope.
function DigDistMsgReader_MessageAreaScan(pScanCfgOpt, pScanMode, pScanScopeChar)
{
var scanScopeChar = "";
if ((typeof(pScanScopeChar) == "string") && /^[SGA]$/.test(pScanScopeChar))
scanScopeChar = pScanScopeChar;
else
{
// Prompt the user to scan in the current sub-board, the current message group,
// or all. Default to all.
console.print(this.text.scanScopePromptText);
scanScopeChar = console.getkeys("SGAC").toString();
// If the user just pressed Enter without choosing anything, then abort and return.
if (scanScopeChar.length == 0)
{
console.crlf();
console.print(this.text.msgScanAbortedText);
console.crlf();
console.pause();
return;
}
}
// Do some logging if verbose logging is enabled
if (gCmdLineArgVals.verboselogging)
{
var logMessage = "Doing a message area scan (";
if (pScanCfgOpt == SCAN_CFG_NEW)
{
// The only valid value for pScanMode in this case is SCAN_NEW, so no
// need to check pScanMode to append more to the log message.
logMessage += "new";
}
else if (pScanCfgOpt == SCAN_CFG_TOYOU)
{
// Valid values for pScanMode in this case are SCAN_UNREAD and SCAN_TOYOU.
if (pScanMode == SCAN_UNREAD)
logMessage += "unread messages to the user";
else if (pScanMode == SCAN_TOYOU)
logMessage += "all messages to the user";
}
if (scanScopeChar == "A") // All sub-boards
logMessage += ", all sub-boards";
else if (scanScopeChar == "G") // Current message group
{
logMessage += ", current message group (" +
msg_area.grp_list[bbs.curgrp].description + ")";
}
else if (scanScopeChar == "S") // Current sub-board
{
logMessage += ", current sub-board (" +
msg_area.grp_list[bbs.curgrp].description + " - " +
msg_area.grp_list[bbs.curgrp].sub_list[bbs.cursub].description + ")";
}
logMessage += ")";
writeToSysAndNodeLog(logMessage);
}
// Save the original search type, sub-board code, searched message headers,
// etc. to be restored later
var originalSearchType = this.searchType;
var originalSubBoardCode = this.subBoardCode;
var originalBBSCurGrp = bbs.curgrp;
var originalBBSCurSub = bbs.cursub;
var originalMsgSrchHdrs = this.msgSearchHdrs;
// Make sure there is no search data
this.ClearSearchData();
// If the object's message base is currently open, then close it. The object's
// message base object will be used to open each sub-board to scan for & read
// unread messages.
if ((this.msgbase != null) && (this.msgbase.is_open))
this.msgbase.close();
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
// Perform the message scan
var continueNewScan = true;
var userAborted = false;
if (scanScopeChar == "A") // All sub-board scan
{
this.doingMultiSubBoardScan = true;
// Iterate through all message groups & sub-boards looking for ones with unread
// messages. When a sub-board with unread messages is found, then let the user
// read messages in that sub-board.
for (var grpIndex = 0; (grpIndex < msg_area.grp_list.length) && continueNewScan; ++grpIndex)
{
// Group description: msg_area.grp_list[grpIndex].description
// Iterate through the sub-boards in this message group looking for unread messages
for (var subIndex = 0; (subIndex < msg_area.grp_list[grpIndex].sub_list.length) && continueNewScan; ++subIndex)
{
// Set the console line counter to 0 to prevent screen pausing
// when the "Searching ..." and "No messages were found" text is
// displayed repeatedly
console.line_counter = 0;
// If the sub-board's access requirements allows the user to read it
// and it's enabled in the user's message scan configuration, then go
// ahead with this sub-board.
// Note: Used to use this to determine whether the user could access the
// sub-board:
//user.compare_ars(msg_area.grp_list[grpIndex].sub_list[subIndex].ars)
// Now using the can_read property.

nightfox
committed
this.setSubBoardCode(msg_area.grp_list[grpIndex].sub_list[subIndex].code); // Needs to be set before getting the last read/scan pointer index
if (msg_area.sub[this.subBoardCode].can_read &&
((msg_area.sub[this.subBoardCode].scan_cfg & pScanCfgOpt) == pScanCfgOpt))
{
// Sub-board description: msg_area.grp_list[grpIndex].sub_list[subIndex].description
// Open the sub-board and check for unread messages. If there are any, then let
// the user read the messages in the sub-board.

nightfox
committed
//this.msgbase = new MsgBase(msg_area.grp_list[grpIndex].sub_list[subIndex].code);
this.msgbase = new MsgBase(this.subBoardCode);
if (this.msgbase.open())
{

nightfox
committed
//this.setSubBoardCode(msg_area.grp_list[grpIndex].sub_list[subIndex].code); // Needs to be set before getting the last read/scan pointer index
// If the current sub-board contains only deleted messages,
// then skip it.
var scanPtrMsgIdx = this.GetScanPtrMsgIdx();
var nonDeletedMsgsExist = (this.FindNextNonDeletedMsgIdx(scanPtrMsgIdx-1, true) > -1);
if (!nonDeletedMsgsExist)
continue;
// In the switch cases below, bbs.curgrp and bbs.cursub are
// temporarily changed the user's sub-board to the current
// sub-board so that certain @-codes (such as @GRP-L@, etc.)
// are displayed by Synchronet correctly.
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
// We might want the starting message index to be different
// depending on the scan mode.
switch (pScanMode)
{
case SCAN_NEW:
// Make sure the sub-board has some messages. Let the user read it if
// the scan pointer index is -1 (one unread message) or if it points to
// a message within the number of messages in the sub-board.
if ((this.msgbase.total_msgs > 0) && ((scanPtrMsgIdx == -1) || (scanPtrMsgIdx < this.msgbase.total_msgs-1)))
{
bbs.curgrp = grpIndex;
bbs.cursub = subIndex;
// Start at the first unread message.
var startMsgIdx = scanPtrMsgIdx + 1;
if (this.SearchingAndResultObjsDefinedForCurSub())
startMsgIdx = 0;
// Allow the user to read messages in this sub-board. Don't allow
// the user to change to a different message area, don't pause
// when there's no search results in a sub-board, and return
// instead of going to the next sub-board via navigation.
var readRetObj = this.ReadOrListSubBoard(null, startMsgIdx, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
}
break;
case SCAN_TOYOU: // All messages to the user
bbs.curgrp = grpIndex;
bbs.cursub = subIndex;
// Search for messages to the user in the current sub-board
// and let the user read the sub-board if messages are
// found. Don't allow the user to change to a different
// message area, don't pause when there's no search results
// in a sub-board, and return instead of going to the next
// sub-board via navigation.
this.searchType = SEARCH_TO_USER_CUR_MSG_AREA;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
break;
case SCAN_UNREAD: // New (unread) messages to the user
bbs.curgrp = grpIndex;
bbs.cursub = subIndex;
// Search for messages to the user in the current sub-board
// and let the user read the sub-board if messages are
// found. Don't allow the user to change to a different
// message area, don't pause when there's no search results
// in a sub-board, and return instead of going to the next
// sub-board via navigation.
this.searchType = SEARCH_TO_USER_NEW_SCAN;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
break;
default:
break;
}
if (this.msgbase != null)
this.msgbase.close();
}
}
}
}
}
else if (scanScopeChar == "G") // Group scan
{
this.doingMultiSubBoardScan = true;
// Iterate through the sub-boards in the current message group looking for messages
for (var subIndex = 0; (subIndex < msg_area.grp_list[bbs.curgrp].sub_list.length) && continueNewScan; ++subIndex)
{
// Set the console line counter to 0 to prevent screen pausing
// when the "Searching ..." and "No messages were found" text is
// displayed repeatedly
console.line_counter = 0;
// If the sub-board's access requirements allows the user to read it
// and it's enabled in the user's message scan configuration, then go
// ahead with this sub-board.

nightfox
committed
this.setSubBoardCode(msg_area.grp_list[bbs.curgrp].sub_list[subIndex].code); // Needs to be set before the last read/scan pointer message
if (msg_area.sub[this.subBoardCode].can_read &&
((msg_area.sub[this.subBoardCode].scan_cfg & pScanCfgOpt) == pScanCfgOpt))
{
// Sub-board description: msg_area.grp_list[bbs.curgrp].sub_list[subIndex].description
// Open the sub-board and check for unread messages. If there are any, then let
// the user read the messages in the sub-board.

nightfox
committed
//this.msgbase = new MsgBase(msg_area.grp_list[bbs.curgrp].sub_list[subIndex].code);
this.msgbase = new MsgBase(this.subBoardCode);
if (this.msgbase.open())
{
// The following line is now done before the 'if' statement above

nightfox
committed
//this.setSubBoardCode(msg_area.grp_list[bbs.curgrp].sub_list[subIndex].code); // Needs to be set before the last read/scan pointer message
// If the current sub-board contains only deleted messages,
// then skip it.
var scanPtrMsgIdx = this.GetScanPtrMsgIdx();
var nonDeletedMsgsExist = (this.FindNextNonDeletedMsgIdx(scanPtrMsgIdx-1, true) > -1);
if (!nonDeletedMsgsExist)
continue;
// Temporarily change the user's sub-board to the current
// sub-board so that certain @-codes (such as @GRP-L@, etc.)
// are displayed by Synchronet correctly.
bbs.curgrp = msg_area.sub[this.subBoardCode].grp_index;
bbs.cursub = msg_area.sub[this.subBoardCode].index;
// We might want the starting message index to be different
// depending on the scan mode.
switch (pScanMode)
{
case SCAN_NEW:
// Make sure the sub-board has some messages. Let the user read it if
// the scan pointer index is -1 (one unread message) or if it points to
// a message within the number of messages in the sub-board.
if ((this.msgbase.total_msgs > 0) && ((scanPtrMsgIdx == -1) || (scanPtrMsgIdx < this.msgbase.total_msgs-1)))
{
//bbs.cursub = subIndex; // Now done a bit earlier
// Start at the first unread message.
var startMsgIdx = scanPtrMsgIdx + 1;
if (this.SearchingAndResultObjsDefinedForCurSub())
startMsgIdx = 0;
// Allow the user to read messages in this sub-board. Don't allow
// the user to change to a different message area, don't pause
// when there's no search results in a sub-board, and return
// instead of going to the next sub-board via navigation.
var readRetObj = this.ReadOrListSubBoard(null, startMsgIdx, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
}
break;
case SCAN_TOYOU: // All messages to the user
//bbs.cursub = subIndex; // Now done a bit earlier
// Search for messages to the user in the current sub-board
// and let the user read the sub-board if messages are found.
// Don't allow the user to change to a different message
// area, don't pause when there's no search results in a
// sub-board, and return instead of going to the next sub-board
// via navigation.
this.searchType = SEARCH_TO_USER_CUR_MSG_AREA;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
break;
case SCAN_UNREAD: // New (unread) messages to the user
//bbs.cursub = subIndex; // Now done a bit earlier
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
// Search for unread messages to the user in the current
// sub-board and let the user read the sub-board if messages
// are found. Don't allow the user to change to a different
// message area, don't pause when there's no search results
// in a sub-board, and return instead of going to the next
// sub-board via navigation.
this.searchType = SEARCH_TO_USER_NEW_SCAN_CUR_GRP;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
break;
default:
break;
}
if (this.msgbase != null)
this.msgbase.close();
}
}
}
}
else if (scanScopeChar == "S") // Current sub-board scan
{
this.doingMultiSubBoardScan = false;

nightfox
committed
// If the command-line arguments don't specify the sub-board code or
// the user is reading personal email, then set the object's sub-board
// code to the user's current sub-board code (bbs.cursub_code) to ensure
// that we open the correct messagebase and so that @-codes, etc. display
// for the correct sub-board.
if (!gCmdLineArgVals.hasOwnProperty("subboard") || gListPersonalEmailCmdLineOpt)
this.setSubBoardCode(bbs.cursub_code);
// Make sure the user has access permissions for the current sub-board and
// has it set up in their scan configuration before letting the user read
// it.

nightfox
committed
if (msg_area.sub[this.subBoardCode].can_read &&
((msg_area.sub[this.subBoardCode].scan_cfg & pScanCfgOpt) == pScanCfgOpt))
{

nightfox
committed
this.msgbase = new MsgBase(this.subBoardCode);
//this.msgbase = new MsgBase(bbs.cursub_code);
if (this.msgbase.open())
{

nightfox
committed
// Temporarily change the user's sub-board to the current
// sub-board so that certain @-codes (such as @GRP-L@, etc.)
// are displayed by Synchronet correctly.
bbs.curgrp = msg_area.sub[this.subBoardCode].grp_index;
bbs.cursub = msg_area.sub[this.subBoardCode].index;
//this.setSubBoardCode(bbs.cursub_code); // Needs to be set before getting the last read/scan pointer message
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
// Only scan this sub-board if it contains messages that are not
// marked as deleted.
var scanPtrMsgIdx = this.GetScanPtrMsgIdx();
var nonDeletedMsgsExist = (this.FindNextNonDeletedMsgIdx(scanPtrMsgIdx-1, true) > -1);
if (nonDeletedMsgsExist)
{
// We might want the starting message index to be different
// depending on the scan mode.
switch (pScanMode)
{
case SCAN_NEW:
// Make sure the sub-board has some messages. Let the user read it if
// the scan pointer index is -1 (one unread message) or if it points to
// a message within the number of messages in the sub-board.
if ((this.msgbase.total_msgs > 0) && ((scanPtrMsgIdx == -1) || (scanPtrMsgIdx < this.msgbase.total_msgs-1)))
{
if (this.subBoardCode != "mail")
bbs.cursub = msg_area.sub[bbs.cursub_code].index;
// Start at the first unread message.
var startMsgIdx = scanPtrMsgIdx + 1;
if (this.SearchingAndResultObjsDefinedForCurSub())
startMsgIdx = 0;
// Allow the user to read messages in this sub-board. Don't allow
// the user to change to a different message area, don't pause
// when there's no search results in a sub-board, and return
// instead of going to the next sub-board via navigation.
var readRetObj = this.ReadOrListSubBoard(null, startMsgIdx, false, true, true);
userAborted = readRetObj.stoppedReading;
}
break;
case SCAN_TOYOU: // All messages to the user
if (this.subBoardCode != "mail")
bbs.cursub = msg_area.sub[bbs.cursub_code].index;
// Set the search type to messages to the user and let the user
// read the sub-board. ReadOrListSubBoard() will do the search.
// Don't allow the user to change to a different message area.
this.searchType = SEARCH_TO_USER_CUR_MSG_AREA;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, true);
userAborted = readRetObj.stoppedReading;
break;
case SCAN_UNREAD: // New (unread) messages to the user
bbs.cursub = msg_area.sub[bbs.cursub_code].index;
// Set the search type to messages to the user and let the user
// read the sub-board. ReadOrListSubBoard() will do the search.
// Don't allow the user to change to a different message area.
this.searchType = SEARCH_TO_USER_NEW_SCAN_CUR_SUB;
bbs.cursub = msg_area.sub[bbs.cursub_code].index;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, true);
userAborted = readRetObj.stoppedReading;
break;
default:
break;
}
}
if (this.msgbase != null)
this.msgbase.close();
}
}
}
// Restore the original sub-board code, searched message headers, etc.
this.searchType = originalSearchType;
this.setSubBoardCode(originalSubBoardCode);
this.msgSearchHdrs = originalMsgSrchHdrs;
bbs.curgrp = originalBBSCurGrp;
bbs.cursub = originalBBSCurSub;
if ((this.msgbase != null) && (this.msgbase.is_open))
this.msgbase.close();
this.msgbase = new MsgBase(this.subBoardCode);
this.doingMultiSubBoardScan = false;
if (this.pauseAfterNewMsgScan)
{
console.crlf();
if (userAborted)
console.print("\1n" + this.text.msgScanAbortedText + "\1n");
else
console.print("\1n" + this.text.msgScanCompleteText + "\1n");
console.crlf();
console.pause();
}
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
}
// For the DigDistMsgReader class: Performs the message reading activity.
//
// Parameters:
// pSubBoardCode: Optional - The internal code of a sub-board to read.
// If not specified, the internal sub-board code specified
// when creating the object will be used.
// pStartingMsgOffset: Optional - The offset of a message to start at
// pReturnOnMessageList: Optional boolean - Whether or not to quit when the
// user wants to list messages (used when this method
// is called from ReadOrListSubBoard()).
// pAllowChgArea: Optional boolean - Whether or not to allow changing the
// message area
// pReturnOnNextAreaNav: Optional boolean - Whether or not this method should
// return when it would move to the next message area due
// navigation from the user (i.e., with the right arrow
// key or with < (go to previous message area) or > (go
// to next message area))
//
// Return value: An object that has the following properties:
// lastUserInput: The user's last keypress/input
// lastAction: The last action chosen by the user based on their
// last keypress, etc.
// stoppedReading: Boolean - Whether reading has stopped
// (due to user quitting, error, or otherwise)
// messageListReturn: Boolean - Whether this method is returning for
// the caller to display the message list. This
// will only be true when the pReturnOnMessageList
// parameter is true and the user wants to list
// messages.
function DigDistMsgReader_ReadMessages(pSubBoardCode, pStartingMsgOffset, pReturnOnMessageList,

nightfox
committed
pAllowChgArea, pReturnOnNextAreaNav)
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
{
var retObj = new Object();
retObj.lastUserInput = "";
retObj.lastAction = ACTION_NONE;
retObj.stoppedReading = false;
retObj.messageListReturn = false;
// If the passed-in sub-board code was different than what was set in the object before,
// then open the new message sub-board.
var previousSubBoardCode = this.subBoardCode;
if (typeof(pSubBoardCode) == "string")
{
if (subBoardCodeIsValid(pSubBoardCode))
this.setSubBoardCode(pSubBoardCode);
else
{
console.print("\1n\1h\1yWarning: \1wThe Message Reader connot continue because an invalid");
console.crlf();
console.print("sub-board code was specified (" + pSubBoardCode + "). Please notify the sysop.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
}
if (this.subBoardCode.length == 0)
{
console.print("\1n\1h\1yWarning: \1wThe Message Reader connot continue because no message");
console.crlf();
console.print("sub-board was specified. Please notify the sysop.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
if (previousSubBoardCode != this.subBoardCode)
{
if ((this.msgbase != null) && (this.msgbase.is_open))
this.msgbase.close();
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
this.msgbase = new MsgBase(this.subBoardCode);
}
else if (this.msgbase == null)
this.msgbase = new MsgBase(this.subBoardCode);
// If the message base was not opened, then output an error and return.
if (!this.msgbase.is_open && !this.msgbase.open())
{
console.print("\1n");
console.crlf();
console.print("\1h\1y* \1wUnable to open message sub-board:");
console.crlf();
console.print(subBoardGrpAndName(this.subBoardCode));
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
// If there are no messages to display in the current sub-board, then let the
// user know and exit.
if (this.NumMessages() == 0)
{
this.msgbase.close();
this.msgbase = null;
console.clear("\1n");
console.center("\1n\1h\1yThere are no messages to display.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
// Check the pAllowChgArea parameter. If it's a boolean, then use it. If
// not, then check to see if we're reading personal mail - If not, then allow
// the user to change to a different message area.
var allowChgMsgArea = true;
if (typeof(pAllowChgArea) == "boolean")
allowChgMsgArea = pAllowChgArea;
else
allowChgMsgArea = (this.subBoardCode != "mail");
// If reading personal email and messages haven't been collected (searched)
// yet, then do so now.
if (this.readingPersonalEmail && (!this.msgSearchHdrs.hasOwnProperty(this.subBoardCode)))
this.msgSearchHdrs[this.subBoardCode] = searchMsgbase(this.subBoardCode, this.msgbase, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
// Determine the index of the message to start at. This will be
// pStartingMsgOffset if pStartingMsgOffset is valid, or the index
// of the user's last-read message in this sub-board.
var msgIndex = 0;
if ((typeof(pStartingMsgOffset) == "number") && (pStartingMsgOffset >= 0) && (pStartingMsgOffset < this.NumMessages()))
msgIndex = pStartingMsgOffset;
else if (this.SearchingAndResultObjsDefinedForCurSub())
msgIndex = 0;
else
{
msgIndex = this.GetLastReadMsgIdx();
if (msgIndex == -1)
msgIndex = 0;
}
// If the current message index is for a message that has been
// deleted, then find the next non-deleted message.
var testMsgHdr = this.GetMsgHdrByIdx(msgIndex);
if ((testMsgHdr == null) || ((testMsgHdr.attr & MSG_DELETE) == MSG_DELETE))
{
// First try going forward
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(msgIndex, true);
// If a non-deleted message was not found, then try going backward.
if (nonDeletedMsgIdx == -1)
nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(msgIndex, false);
// If a non-deleted message was found, then set msgIndex to it.
// Otherwise, tell the user there are no messages in this sub-board
// and return.
if (nonDeletedMsgIdx > -1)
msgIndex = nonDeletedMsgIdx;
else
{
this.msgbase.close();
this.msgbase = null;
console.clear("\1n");
console.center("\1h\1yThere are no messages to display.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
}
// Construct the hotkey help line (needs to be done after the message
// base is open so that the delete & edit keys can be added correctly).
this.SetEnhancedReaderHelpLine();
// Get the screen ready for reading messages - First, clear the screen.
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
console.clear("\1n");
// Display the help line at the bottom of the screen
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Input loop
var msgHdr = null;
var dateTimeStr = null;
var screenY = 1; // For screen updates requiring more than one line
var continueOn = true;
var readMsgRetObj = null;
// previousNextAction will store the next action from the previous iteration.
// It is useful for some checks, such as when the current message is deleted,
// we'll want to see if the user wanted to go to the previous message/area
// for navigation purposes.
var previousNextAction = ACTION_NONE;
while (continueOn && (msgIndex >= 0) && (msgIndex < this.NumMessages()))
{
// Display the message with the enhanced read method
readMsgRetObj = this.ReadMessageEnhanced(msgIndex, allowChgMsgArea);
retObj.lastUserInput = readMsgRetObj.lastKeypress;
retObj.lastAction = readMsgRetObj.nextAction;
// If we should refresh the enhanced reader help line on the screen (and
// the returned message offset is valid and the user's terminal supports ANSI),
// then refresh the help line.
if (readMsgRetObj.refreshEnhancedRdrHelpLine && readMsgRetObj.offsetValid && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// If the returned message offset is invalid, then quit.
if (!readMsgRetObj.offsetValid)
{
continueOn = false;
retObj.stoppedReading = true;
break;
}
// If the message is marked as deleted (not by the user), then go to the
// next/previous message
else if (readMsgRetObj.msgDeleted)
{
// If the user's next action in the last iteration was to go to the
// previous message, then go backwards; otherwise, go forward.
if (previousNextAction == ACTION_GO_PREVIOUS_MSG)

nightfox
committed
msgIndex = this.FindNextNonDeletedMsgIdx(msgIndex, false);
else

nightfox
committed
msgIndex = this.FindNextNonDeletedMsgIdx(msgIndex, true);
continueOn = ((msgIndex >= 0) && (msgIndex < this.NumMessages()));
}
else if (readMsgRetObj.nextAction == ACTION_QUIT) // Quit
{
// Quit
continueOn = false;
retObj.stoppedReading = true;
break;
}
else if (readMsgRetObj.lastKeypress == "R")
{
// Replying to the message is handled in ReadMessageEnhanced().
// The help line at the bottom of the screen needs to be redrawn though,
// for ANSI users.
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))

nightfox
committed
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else if (readMsgRetObj.nextAction == ACTION_GO_PREVIOUS_MSG) // Go to previous message/area
{
// TODO: There is some opportunity for screen redraw optimization - If
// already at the first readable sub-board, this would redraw the
// screen unnecessarily. Similar for the right arrow key too.
// The newMsgOffset value will be 0 or more if a prior non-deleted
// message was found. If it's -1, then allow going to the previous
// message sub-board/group.
if (readMsgRetObj.newMsgOffset > -1)

nightfox
committed
msgIndex = readMsgRetObj.newMsgOffset;
else
{
// The user is at the beginning of the current sub-board.
if (allowChgMsgArea)
{
var goToPrevRetval = this.GoToPrevSubBoardForEnhReader(allowChgMsgArea);
retObj.stoppedReading = goToPrevRetval.shouldStopReading;
// If we're going to stop reading, then
if (retObj.stoppedReading)
msgIndex = 0;
else if (goToPrevRetval.changedMsgArea)

nightfox
committed
msgIndex = goToPrevRetval.msgIndex;
}
// If the caller wants this method to return instead of going to the next
// sub-board with messages, then do so.
if (pReturnOnNextAreaNav)

nightfox
committed
return retObj;
}
}
// Go to next message action - This can happen with the right arrow key or
// if the user deletes the message in the ReadMessageEnhanced() method.
else if (readMsgRetObj.nextAction == ACTION_GO_NEXT_MSG)
{
// The newMsgOffset value will be 0 or more if a later non-deleted
// message was found. If it's -1, then allow going to the next
// message sub-board/group.
if (readMsgRetObj.newMsgOffset > -1)

nightfox
committed
msgIndex = readMsgRetObj.newMsgOffset;
else
{
// The user is at the end of the current sub-board.
if (allowChgMsgArea && !pReturnOnNextAreaNav)
{
var goToNextRetval = this.GoToNextSubBoardForEnhReader(allowChgMsgArea);
retObj.stoppedReading = goToNextRetval.shouldStopReading;
// If we're going to stop reading, then
if (retObj.stoppedReading)
msgIndex = 0;
else if (goToNextRetval.changedMsgArea)

nightfox
committed
msgIndex = goToNextRetval.msgIndex;
}
// If the caller wants this method to return instead of going to the next
// sub-board with messages, then do so.
if (pReturnOnNextAreaNav)

nightfox
committed
return retObj;
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
}
}
else if (readMsgRetObj.nextAction == ACTION_GO_FIRST_MSG) // Go to the first message
{
// Go to the first message that's not marked as deleted. This passes -1 as the
// starting message index because FindNextNonDeletedMsgIdx() will increment it
// before searching in order to find the "next" message.
msgIndex = this.FindNextNonDeletedMsgIdx(-1, true);
}
else if (readMsgRetObj.nextAction == ACTION_GO_LAST_MSG) // Go to the last message
{
// Go to the last message that's not marked as deleted
msgIndex = this.FindNextNonDeletedMsgIdx(this.NumMessages(), false);
}
else if (readMsgRetObj.nextAction == ACTION_CHG_MSG_AREA) // Change message area, if allowed
{
if (allowChgMsgArea)
{
// Change message sub-board. If a different sub-board was
// chosen, then change some variables to use the new
// chosen sub-board.
this.SelectMsgArea();
var chgSubBoardRetObj = this.EnhancedReaderChangeSubBoard(bbs.cursub_code);
if (chgSubBoardRetObj.succeeded)
{
// Set the message index, etc.
// If there are search results, then set msgIndex to the first
// message. Otherwise (if there is no search specified), then
// set the message index to the user's last read message.
if (this.SearchingAndResultObjsDefinedForCurSub())

nightfox
committed
msgIndex = 0;
else

nightfox
committed
msgIndex = chgSubBoardRetObj.lastReadMsgIdx;
// If the current message index is for a message that has been
// deleted, then find the next non-deleted message.
testMsgHdr = this.GetMsgHdrByIdx(msgIndex);
if ((testMsgHdr == null) || ((testMsgHdr.attr & MSG_DELETE) == MSG_DELETE))
{
// First try going forward
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(msgIndex, true);
// If a non-deleted message was not found, then try going backward.
if (nonDeletedMsgIdx == -1)

nightfox
committed
nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(msgIndex, false);
// If a non-deleted message was found, then set msgIndex to it.
// Otherwise, return.
// Note: If there are no messages in the chosen sub-board at all,
// then the error would have already been shown.
if (nonDeletedMsgIdx > -1)

nightfox
committed
msgIndex = nonDeletedMsgIdx;
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
else
{
if (this.NumMessages() != 0)
{
// There are messages, but none that are not deleted.
console.clear("\1n");
console.center("\1h\1yThere are no messages to display.");
console.crlf();
console.pause();
}
this.msgbase.close();
retObj.stoppedReading = true;
return retObj;
}
}
// Set the hotkey help line again, since the new sub-board might have
// different settings for whether messages can be edited or deleted,
// then refresh it on the screen.
var oldHotkeyHelpLine = this.enhReadHelpLine;
this.SetEnhancedReaderHelpLine();
if ((oldHotkeyHelpLine != this.enhReadHelpLine) && this.scrollingReaderInterface && console.term_supports(USER_ANSI))

nightfox
committed
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
{
retObj.stoppedReading = false;
return retObj;
}
}
}
else if (readMsgRetObj.nextAction == ACTION_GO_PREV_MSG_AREA) // Go to the previous message area
{
// The user is at the beginning of the current sub-board.
if (allowChgMsgArea)
{
var goToPrevRetval = this.GoToPrevSubBoardForEnhReader(allowChgMsgArea);
retObj.stoppedReading = goToPrevRetval.shouldStopReading;
// If we're going to stop reading, then
if (retObj.stoppedReading)
msgIndex = 0;
else if (goToPrevRetval.changedMsgArea)

nightfox
committed
msgIndex = goToPrevRetval.msgIndex;
}
// If the caller wants this method to return instead of going to the next
// sub-board with messages, then do so.
if (pReturnOnNextAreaNav)

nightfox
committed
return retObj;
}
else if (readMsgRetObj.nextAction == ACTION_GO_NEXT_MSG_AREA) // Go to the next message area
{
if (allowChgMsgArea && !pReturnOnNextAreaNav)
{
var goToNextRetval = this.GoToNextSubBoardForEnhReader(allowChgMsgArea);
retObj.stoppedReading = goToNextRetval.shouldStopReading;
if (retObj.stoppedReading)
msgIndex = 0;
else if (goToNextRetval.changedMsgArea)

nightfox
committed
msgIndex = goToNextRetval.msgIndex;
}
// If the caller wants this method to return instead of going to the next
// sub-board with messages, then do so.
if (pReturnOnNextAreaNav)

nightfox
committed
return retObj;
}
else if (readMsgRetObj.nextAction == ACTION_DISPLAY_MSG_LIST) // Display message list
{
// If we need to return to the caller for this, then do so.
if (pReturnOnMessageList)
{
retObj.messageListReturn = true;
return retObj;
}
else
{
// If this.reverseListOrder is the string "ASK", the user will be prompted
// on the last line of the screen for whether they want to list the
// messages in reverse order. So, erase the help line on the bottom of
// the screen.
if ((typeof(this.reverseListOrder) == "string") && (this.reverseListOrder.toUpperCase() == "ASK"))
{
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol("\1n");
}
}
// Call the ListMessages method - Don't change the sub-board, and
// have it return if the user chooses a message to read.

nightfox
committed
var listRetObj = this.ListMessages(null, true, pAllowChgArea);
// If the user wants to quit, then stop the input loop.
if (listRetObj.lastUserInput == "Q")
{
continueOn = false;
retObj.stoppedReading = true;
}
// If the user chose a different message, then set the message index
else if ((listRetObj.selectedMsgOffset > -1) && (listRetObj.selectedMsgOffset < this.NumMessages()))

nightfox
committed
msgIndex = listRetObj.selectedMsgOffset;
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
}
}
// Go to specific message & new message offset is valid: Read the new
// message
else if ((readMsgRetObj.nextAction == ACTION_GO_SPECIFIC_MSG) && (readMsgRetObj.newMsgOffset > -1))
{
// The user selected a different message in this sub-board
msgIndex = readMsgRetObj.newMsgOffset;
}
// Save this iteration's next action for the "previous" next action for the next iteration
previousNextAction = readMsgRetObj.nextAction;
}
return retObj;
}
// For the DigDistMsgReader class: Performs the message listing, given a
// sub-board code.
//
// Paramters:
// pSubBoardCode: Optional - The internal sub-board code, or "mail"
// for personal email.

nightfox
committed
// pReturnOnMsgSelect: Optional - A boolean to specify whether or not to
// return when a message is selected to read. Defaults
// to false.
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
// Return value: An object containing the following properties:
// lastUserInput: The user's last keypress/input
// selectedMsgOffset: The index of the message selected to read,
// if one was selected. If none was selected,
// this will be -1.

nightfox
committed
function DigDistMsgReader_ListMessages(pSubBoardCode, pReturnOnMsgSelect, pAllowChgSubBoard)
{
var retObj = new Object();
retObj.lastUserInput = "";
retObj.selectedMsgOffset = -1;
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
// If the passed-in sub-board code was different than what was set in the object before,
// then open the new message sub-board.
var previousSubBoardCode = this.subBoardCode;
if (typeof(pSubBoardCode) == "string")
{
if (subBoardCodeIsValid(pSubBoardCode))
this.setSubBoardCode(pSubBoardCode);
else
{
console.print("\1n\1h\1yWarning: \1wThe Message Reader connot continue because an invalid");
console.crlf();
console.print("sub-board code was specified (" + pSubBoardCode + "). Please notify the sysop.");
console.crlf();
console.pause();
return retObj;
}
}
if (this.subBoardCode.length == 0)
{
console.print("\1n\1h\1yWarning: \1wThe Message Reader connot continue because no message\r\n");
console.print("sub-board was specified. Please notify the sysop.\r\n\1p");
return retObj;
}
if (previousSubBoardCode != this.subBoardCode)
{
this.msgbase = null;
this.msgbase = new MsgBase(this.subBoardCode);
}
var openSucceeded = true;
if (!this.msgbase.is_open)
openSucceeded = this.msgbase.open();
if (openSucceeded)
{
// If there are no messages to display in the current sub-board, then let the
// user know and exit.
if (this.NumMessages() == 0)
{
this.msgbase.close();
this.msgbase = null;
console.clear("\1n");
console.center("\1n\1h\1yThere are no messages to display.\r\n\1p");
return retObj;
}
// Construct the traditional UI pause text and the line of help text for lightbar
// mode. This adds the delete and edit keys if the user is allowed to delete & edit
// messages.
this.SetMsgListPauseTextAndLightbarHelpLine();
// If this.reverseListOrder is the string "ASK", prompt the user for whether
// they want to list the messages in reverse order.
if ((typeof(this.reverseListOrder) == "string") && (this.reverseListOrder.toUpperCase() == "ASK"))
{
if (numMessages(bbs.cursub_code) > 0)
this.reverseListOrder = !console.noyes("\1n\1cList in reverse (newest on top)");
}
// List the messages using the lightbar or traditional interface, depending on
// what this.msgListUseLightbarListInterface is set to. The lightbar interface requires ANSI.
if (this.msgListUseLightbarListInterface && canDoHighASCIIAndANSI())

nightfox
committed
retObj = this.ListMessages_Lightbar(pReturnOnMsgSelect, pAllowChgSubBoard);
else

nightfox
committed
retObj = this.ListMessages_Traditional(pReturnOnMsgSelect, pAllowChgSubBoard);
return retObj;
}
// For the DigDistMsgReader class: Performs the message listing, given a
// sub-board code. This version uses a traditional user interface, prompting
// the user at the end of each page to continue, quit, or read a message.
// Note: This function requires this.msgbase to be valid and open.
//
// Parameters:
// pReturnOnMsgSelect: Optional - A boolean to specify whether or not
// to return when a message is selected to read.

nightfox
committed
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
//
// Return value: An object containing the following properties:
// lastUserInput: The user's last keypress/input
// selectedMsgOffset: The index of the message selected to read,
// if one was selected. If none was selected,
// this will be -1.

nightfox
committed
function DigDistMsgReader_ListMessages_Traditional(pReturnOnMsgSelect, pAllowChgSubBoard)
{

nightfox
committed
var retObj = new Object();
retObj.lastUserInput = "";
retObj.selectedMsgOffset = -1;
// Reset this.readAMessage and deniedReadingmessage to false, in case the
// message listing has previously ended with them set to true.
this.readAMessage = false;
this.deniedReadingMessage = false;
// this.msgbase must be valid before continuing.
if ((typeof(this.msgbase) == "undefined") || (this.msgbase == null))
{

nightfox
committed
console.center("\1n\1h\1yError: \1wUnable to list messages because the sub-board is not open.\r\n\1p");
return retObj;
}
else if (!this.msgbase.is_open)
{

nightfox
committed
console.center("\1n\1h\1yError: \1wUnable to list messages because the sub-board is not open.\r\n\1p");
return retObj;
}

nightfox
committed
var allowChgSubBoard = (typeof(pAllowChgSubBoard) == "boolean" ? pAllowChgSubBoard : true);
// this.tradMsgListNumLines stores the maximum number of lines to write. It's the number
// of rows on the user's screen - 3 to make room for the header line
// at the top, the question line at the bottom, and 1 extra line at
// the bottom of the screen so that displaying carriage returns
// doesn't mess up the position of the header lines at the top.
this.tradMsgListNumLines = console.screen_rows-3;
var nListStartLine = 2; // The first line number on the screen for the message list
// If we will be displaying the message group and sub-board in the
// header at the top of the screen (an additional 2 lines), then
// update this.tradMsgListNumLines and nListStartLine to account for this.
if (this.displayBoardInfoInHeader)
{
this.tradMsgListNumLines -= 2;
nListStartLine += 2;
}

nightfox
committed
// If the user's terminal doesn't support ANSI, then re-calculate
// this.tradMsgListNumLines - we won't be keeping the headers at the top of the
// screen.
if (!canDoHighASCIIAndANSI()) // Could also be !console.term_supports(USER_ANSI)
this.tradMsgListNumLines = console.screen_rows - 2;
// Clear the screen and write the header at the top
console.clear("\1n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
// If this.tradListTopMsgIdx hasn't been set yet, then get the index of the user's
// last read message and figure out which page it's on and set the top message
// index accordingly.
if (this.tradListTopMsgIdx == -1)

nightfox
committed
this.SetUpTraditionalMsgListVars();
// Write the message list
var continueOn = true;

nightfox
committed
var retvalObj = null;
var curpos = null; // Current character position
var lastScreen = false;
while (continueOn)
{
// Go to the top and write the current page of message information,
// then update curpos.
console.gotoxy(1, nListStartLine);
lastScreen = this.ListScreenfulOfMessages(this.tradListTopMsgIdx, this.tradMsgListNumLines);
curpos = console.getxy();
clearToEOS(curpos.y);
console.gotoxy(curpos);
// Prompt the user whether or not to continue or to read a message
// (by message number).
if (this.reverseListOrder)

nightfox
committed
retvalObj = this.PromptContinueOrReadMsg((this.tradListTopMsgIdx == this.NumMessages()-1), lastScreen, pReturnOnMsgSelect, allowChgSubBoard);
else
retvalObj = this.PromptContinueOrReadMsg((this.tradListTopMsgIdx == 0), lastScreen, pReturnOnMsgSelect, allowChgSubBoard);
retObj.lastUserInput = retvalObj.userInput;
retObj.selectedMsgOffset = retvalObj.selectedMsgOffset;
continueOn = retvalObj.continueOn;
// TODO: Update this to use PageUp & PageDown keys for paging? It would
// require updating PromptContinueOrReadMsg(), which would be non-trivial
// because that method uses console.getkeys() with a list of allowed keys
// and a message number limit.
if (continueOn)
{
// If the user chose to go to the previous page of listings,
// then subtract the appropriate number of messages from
// this.tradListTopMsgIdx in order to do so.
if (retvalObj.userInput == "P")
{
if (this.reverseListOrder)

nightfox
committed
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
{
this.tradListTopMsgIdx += this.tradMsgListNumLines;
// If we go past the beginning, then we need to reset
// msgNum so we'll be at the beginning of the list.
var totalNumMessages = this.NumMessages();
if (this.tradListTopMsgIdx >= totalNumMessages)
this.tradListTopMsgIdx = totalNumMessages - 1;
}
else
{
this.tradListTopMsgIdx -= this.tradMsgListNumLines;
// If we go past the beginning, then we need to reset
// msgNum so we'll be at the beginning of the list.
if (this.tradListTopMsgIdx < 0)
this.tradListTopMsgIdx = 0;
}
}
// If the user chose to go to the next page, update
// this.tradListTopMsgIdx appropriately.
else if (retvalObj.userInput == "N")
{
if (this.reverseListOrder)

nightfox
committed
this.tradListTopMsgIdx -= this.tradMsgListNumLines;
else
this.tradListTopMsgIdx += this.tradMsgListNumLines;
}
// First page
else if (retvalObj.userInput == "F")
{
if (this.reverseListOrder)

nightfox
committed
this.tradListTopMsgIdx = this.NumMessages() - 1;
else
this.tradListTopMsgIdx = 0;
}
// Last page
else if (retvalObj.userInput == "L")
{
if (this.reverseListOrder)

nightfox
committed
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
{
this.tradListTopMsgIdx = (this.NumMessages() % this.tradMsgListNumLines) - 1;
// If this.tradListTopMsgIdx is now invalid (below 0), then adjust it
// to properly display the last page of messages.
if (this.tradListTopMsgIdx < 0)
this.tradListTopMsgIdx = this.tradMsgListNumLines - 1;
}
else
{
var totalNumMessages = this.NumMessages();
this.tradListTopMsgIdx = totalNumMessages - (totalNumMessages % this.tradMsgListNumLines);
if (this.tradListTopMsgIdx >= totalNumMessages)
this.tradListTopMsgIdx = totalNumMessages - this.tradMsgListNumLines;
}
}
// D: Delete a message
else if (retvalObj.userInput == "D")
{
if (this.CanDelete() || this.CanDeleteLastMsg())
{
var msgNum = this.PromptForMsgNum({ x: curpos.x, y: curpos.y+1 }, this.text.deleteMsgNumPromptText, false, ERROR_PAUSE_WAIT_MS, false);
// If the user enters a valid message number, then call the
// DeleteMessage() method, which will prompt the user for
// confirmation and delete the message if confirmed.
if (msgNum > 0)
this.DeleteMessage(msgNum-1);

nightfox
committed
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
}
}
// E: Edit a message
else if (retvalObj.userInput == "E")
{
if (this.CanEdit())
{
var msgNum = this.PromptForMsgNum({ x: curpos.x, y: curpos.y+1 }, this.text.editMsgNumPromptText, false, ERROR_PAUSE_WAIT_MS, false);
// If the user entered a valid message number, then let the
// user edit the message.
if (msgNum > 0)
var returnObj = this.EditExistingMsg(msgNum-1);
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
}
}
// G: Go to a specific message by # (place that message on the top)
else if (retvalObj.userInput == "G")
{
var msgNum = this.PromptForMsgNum(curpos, "\1n" + this.text.goToMsgNumPromptText, false, ERROR_PAUSE_WAIT_MS, false);
if (msgNum > 0)
this.tradListTopMsgIdx = msgNum - 1;

nightfox
committed
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
}
// ?: Display help
else if (retvalObj.userInput == "?")
{
console.clear("\1n");
this.DisplayMsgListHelp(allowChgSubBoard, true);
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
}
// C: Change to another message area (sub-board)
else if (retvalObj.userInput == "C")
{
if (allowChgSubBoard && (this.subBoardCode != "mail"))
{
// Store the current sub-board code so we can see if it changed
var oldSubCode = bbs.cursub_code;
// Let the user choose another message area. If they chose
// a different message area, then set up the message base
// object accordingly.
this.SelectMsgArea();
if (bbs.cursub_code != oldSubCode)
{
var chgSubRetval = this.ChangeSubBoard(bbs.cursub_code);
continueOn = chgSubRetval.succeeded;
}
// Update the traditional list variables and refresh the screen
if (continueOn)
{
this.SetUpTraditionalMsgListVars();
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
}
}
}
else
{
// If pReturnOnMsgSelect is true and the user selected a message to
// read, then exit out of this input loop so we can return from
// this method - The calling method will call the enhanced reader
// method.
if (pReturnOnMsgSelect && (retObj.selectedMsgOffset >= 0))
continueOn = false;
}

nightfox
committed
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
if (!pReturnOnMsgSelect)
{
// If the user chose to read a message or denied confirmation, then:
// - Re-draw the column headers at the top of the screen.
// - Subtract this.tradMsgListNumLines from msgNum so that this script displays
// the same page where the user left off.
if (this.readAMessage || this.deniedReadingMessage)
{
if (canDoHighASCIIAndANSI()) // Could also be console.term_supports(USER_ANSI)
this.WriteMsgListScreenTopHeader();
}
this.readAMessage = false;
this.deniedReadingMessage = false;
// If the user's terminal doesn't support ANSI, then adjust
// this.tradMsgListNumLines to 1 less than the number of screen rows, because
// after the first page, we no longer need to display the message
// list header line.
if (!canDoHighASCIIAndANSI()) // Could also be !console.term_supports(USER_ANSI)
this.tradMsgListNumLines = console.screen_rows - 1;
}
}
}

nightfox
committed
return retObj;
}
// For the DigDistMsgReader class: Performs the message listing, given a
// sub-board code. This verison uses a lightbar interface for message
// navigation. Note: This function requires this.msgbase to be valid and
// open.
//
// Parameters:
// pReturnOnMsgSelect: Optional - A boolean to specify whether or not
// to return when a message is selected to read.

nightfox
committed
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
//
// Return value: An object containing the following properties:
// lastUserInput: The user's last keypress/input
// selectedMsgOffset: The index of the message selected to read,
// if one was selected. If none was selected,
// this will be -1.

nightfox
committed
function DigDistMsgReader_ListMessages_Lightbar(pReturnOnMsgSelect, pAllowChgSubBoard)
{

nightfox
committed
var retObj = new Object();
retObj.lastUserInput = "";
retObj.selectedMsgOffset = -1;
// This method is only supported if the user's terminal supports
// ANSI.
if (!canDoHighASCIIAndANSI()) // Could also be !console.term_supports(USER_ANSI)
{
console.print("\r\n\1h\1ySorry, an ANSI terminal is required for this operation.\1n\1w\r\n");
console.pause();
return retObj;
}
// Reset this.readAMessage and deniedReadingMessage to false, in case the
// message listing has previously ended with them set to true.
this.readAMessage = false;
this.deniedReadingMessage = false;

nightfox
committed
// this.msgbase must be valid before continuing.
if ((typeof(this.msgbase) == "undefined") || (this.msgbase == null))
{

nightfox
committed
console.center("\1n\1h\1yError: \1wUnable to list messages because the sub-board is not open.\r\n\1p");
return retObj;
}
else if (!this.msgbase.is_open)
{

nightfox
committed
console.center("\1n\1h\1yError: \1wUnable to list messages because the sub-board is not open.\r\n\1p");
return retObj;
}

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

nightfox
committed
// This function will be used for displaying the help line at
// the bottom of the screen.
function DisplayHelpLine(pHelpLineText)
{
console.gotoxy(1, console.screen_rows);
console.print(pHelpLineText);
console.cleartoeol("\1n");
}

nightfox
committed
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
// Clear the screen and write the header at the top
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
// If the lightbar message list index & cursor position variables haven't been
// set yet, then set them.
if ((this.lightbarListTopMsgIdx == -1) || (this.lightbarListSelectedMsgIdx == -1) ||
(this.lightbarListCurPos == null))
{
this.SetUpLightbarMsgListVars();
}
// List a screenful of message headers
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
var lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
// Move the cursor to where it needs to be
console.gotoxy(this.lightbarListCurPos);
// User input loop
var bottomMsgIndex = 0;
var userInput = "";
var msgHeader = null;
var continueOn = true;
while (continueOn)
{
bbs.command_str = ""; // To prevent weirdness
retObj.selectedMsgOffset = -1;
// Calculate the message number (0-based) of the message
// appearing on the bottom of the screen.
if (this.reverseListOrder)
{

nightfox
committed
bottomMsgIndex = this.lightbarListTopMsgIdx - this.lightbarMsgListNumLines + 1;
if (bottomMsgIndex < 0)
bottomMsgIndex = 0;
}
else
{

nightfox
committed
var totalNumMessages = this.NumMessages();
bottomMsgIndex = this.lightbarListTopMsgIdx + this.lightbarMsgListNumLines - 1;
if (bottomMsgIndex >= totalNumMessages)
bottomMsgIndex = totalNumMessages - 1;
}

nightfox
committed
// Write the current message information with highlighting colors
msgHeader = this.GetMsgHdrByIdx(this.lightbarListSelectedMsgIdx);
this.PrintMessageInfo(msgHeader, true, this.lightbarListSelectedMsgIdx+1);
console.gotoxy(this.lightbarListCurPos); // Make sure the cursor is still in the right place

nightfox
committed
// Get a key from the user (upper-case) and take appropriate action.
userInput = getKeyWithESCChars(K_UPPER|K_NOCRLF|K_NOECHO|K_NOSPIN);
retObj.lastUserInput = userInput;
// Q: Quit
if (userInput == "Q")
{
// Quit
continueOn = false;
break;
}
// ?: Show help
else if (userInput == "?")
{
// Display help
console.clear("\1n");
this.DisplayMsgListHelp(allowChgSubBoard, true);

nightfox
committed
// Re-draw the message list on the screen
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(this.lightbarListCurPos); // Put the cursor back where it should be
}
// Up arrow: Highlight the previous message
else if (userInput == KEY_UP)
{
// Make sure this.lightbarListSelectedMsgIdx is within bounds before moving down.
if (this.reverseListOrder)

nightfox
committed
{
if (this.lightbarListSelectedMsgIdx >= this.NumMessages() - 1)
continue;
}
else
{
if (this.lightbarListSelectedMsgIdx <= 0)
continue;
}

nightfox
committed
// Print the current message information with regular colors
this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
if (this.reverseListOrder)

nightfox
committed
++this.lightbarListSelectedMsgIdx;
else
--this.lightbarListSelectedMsgIdx;

nightfox
committed
// If the current screen row is above the first line allowed, then
// move the cursor up one row.
if (this.lightbarListCurPos.y > this.lightbarMsgListStartScreenRow)
{
console.gotoxy(1, this.lightbarListCurPos.y-1);
this.lightbarListCurPos.x = 1;
--this.lightbarListCurPos.y;
}
else
{
// Go onto the previous page, with the cursor highlighting
// the last message on the page.
if (this.reverseListOrder)

nightfox
committed
this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx + this.lightbarMsgListNumLines - 1;
else
this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx - this.lightbarMsgListNumLines + 1;

nightfox
committed
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(1, this.lightbarMsgListStartScreenRow+this.lightbarMsgListNumLines-1);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow+this.lightbarMsgListNumLines-1;
}
}
// Down arrow: Highlight the next message
else if (userInput == KEY_DOWN)
{
// Make sure this.lightbarListSelectedMsgIdx is within bounds before moving down.
if (this.reverseListOrder)

nightfox
committed
{
if (this.lightbarListSelectedMsgIdx <= 0)
continue;
}
else
{
if (this.lightbarListSelectedMsgIdx >= this.NumMessages() - 1)
continue;
}

nightfox
committed
// Print the current message information with regular colors
this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
if (this.reverseListOrder)

nightfox
committed
--this.lightbarListSelectedMsgIdx;
else
++this.lightbarListSelectedMsgIdx;

nightfox
committed
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
// If the current screen row is below the last line allowed, then
// move the cursor down one row.
if (this.lightbarListCurPos.y < this.lightbarMsgListStartScreenRow+this.lightbarMsgListNumLines-1)
{
console.gotoxy(1, this.lightbarListCurPos.y+1);
this.lightbarListCurPos.x = 1;
++this.lightbarListCurPos.y;
}
else
{
// Go onto the next page, with the cursor highlighting
// the first message on the page.
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx;
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
// If we were on the last page, then clear the screen from
// the current line to the end of the screen.
if (lastPage)
{
this.lightbarListCurPos = console.getxy();
clearToEOS(this.lightbarListCurPos.y);
// Make sure the help line is still there
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}

nightfox
committed
// Move the cursor to the top of the list
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
}
// HOME key: Go to the first message on the screen
else if (userInput == KEY_HOME)
{
// Print the current message information with regular colors
this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
// Go to the first message of the current page
if (this.reverseListOrder)

nightfox
committed
this.lightbarListSelectedMsgIdx += (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
else
this.lightbarListSelectedMsgIdx -= (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
// Move the cursor to the first message line
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
// END key: Go to the last message on the screen
else if (userInput == KEY_END)
{
// Print the current message information with regular colors
this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
// Update the selected message #
this.lightbarListSelectedMsgIdx = bottomMsgIndex;
// Go to the last message of the current page
if (this.reverseListOrder)

nightfox
committed
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + this.lightbarListTopMsgIdx - bottomMsgIndex;
else
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + bottomMsgIndex - this.lightbarListTopMsgIdx;
console.gotoxy(this.lightbarListCurPos);
}
// Enter key: Select a message to read
else if (userInput == KEY_ENTER)
{
var originalCurpos = console.getxy();

nightfox
committed
// Allow the user to read the current message.
var readMsg = true;
if (this.promptToReadMessage)
{
// Confirm with the user whether to read the message.
var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ +(msgHeader.offset+1)
+ this.colors["readMsgConfirmColor"]
+ ": Are you sure";
console.gotoxy(1, console.screen_rows);
console.print("\1n");
console.clearline();
readMsg = console.yesno(sReadMsgConfirmText);
}
var repliedToMessage = false;
if (readMsg)
{
// If there is a search specified and the search result objects are
// set up for the current sub-board, then the selected message offset
// should be the search result array index. Otherwise (if not
// searching), the message offset should be the actual message offset
// in the message base.
if (this.SearchingAndResultObjsDefinedForCurSub())
retObj.selectedMsgOffset = this.lightbarListSelectedMsgIdx;
else
retObj.selectedMsgOffset = msgHeader.offset;

nightfox
committed
if (pReturnOnMsgSelect)
return retObj;
else
{
this.readAMessage = true;
console.clear("\1n");
var readRetObj = null;
if (this.SearchingAndResultObjsDefinedForCurSub())
readRetObj = this.ReadMessage(this.lightbarListSelectedMsgIdx);

nightfox
committed
else
readRetObj = this.ReadMessage(msgHeader.offset);

nightfox
committed
repliedToMessage = readRetObj.userReplied;
}
}
else
this.deniedReadingMessage = true;

nightfox
committed
// Ask the user if they want to continue reading messages
if (this.promptToContinueListingMessages)
{
continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +
"Continue listing messages");
}
// If the user chose to continue reading messages, then refresh
// the screen. Even if the user chooses not to read the message,
// the screen needs to be re-drawn so it appears properly.
if (continueOn)
{
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
// If we're dispaying in reverse order and the user replied
// to the message, then we'll have to re-arrange the screen
// a bit to make way for the new message that will appear
// in the list.
if (this.reverseListOrder && repliedToMessage)

nightfox
committed
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
{
// Make way for the new message, which will appear at the
// top.
++this.lightbarListTopMsgIdx;
// If the cursor is below the bottommost line displaying
// messages, then advance the cursor down one position.
// Otherwise, increment this.lightbarListSelectedMsgIdx (since a new message
// will appear at the top, the previous selected message
// will be pushed to the next page).
if (this.lightbarListCurPos.y < console.screen_rows - 1)
{
++originalCurpos.y;
++this.lightbarListCurPos.y;
}
else
++this.lightbarListSelectedMsgIdx;
}
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(originalCurpos); // Put the cursor back where it should be
}
}
// PageDown: Next page
else if (userInput == KEY_PAGE_DOWN)
{
// Next page
if (!lastPage)
{
if (this.reverseListOrder)

nightfox
committed
this.lightbarListTopMsgIdx -= this.lightbarMsgListNumLines;
else
this.lightbarListTopMsgIdx += this.lightbarMsgListNumLines;
this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
// If we were on the last page, then clear the screen from
// the current line to the end of the screen.
if (lastPage)
{
this.lightbarListCurPos = console.getxy();
clearToEOS(this.lightbarListCurPos.y);
// Make sure the help line is still there
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}

nightfox
committed
// Move the cursor back to the first message info line
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
else {
// The user is on the last page - Go to the last message on the page.
if (this.lightbarListSelectedMsgIdx != bottomMsgIndex)
{
// Print the current message information with regular colors
this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
// Update the selected message #
this.lightbarListSelectedMsgIdx = bottomMsgIndex;
this.lightbarListCurPos.x = 1;
if (this.reverseListOrder)
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + this.lightbarListTopMsgIdx - bottomMsgIndex;
else
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + bottomMsgIndex - this.lightbarListTopMsgIdx;
console.gotoxy(this.lightbarListCurPos);
}
}

nightfox
committed
}
// PageUp: Previous page
else if (userInput == KEY_PAGE_UP)
{
var canGoToPrevious = false;
if (this.reverseListOrder)

nightfox
committed
canGoToPrevious = (this.lightbarListTopMsgIdx < this.NumMessages() - 1);
else
canGoToPrevious = (this.lightbarListTopMsgIdx > 0);
if (canGoToPrevious)

nightfox
committed
{
if (this.reverseListOrder)

nightfox
committed
this.lightbarListTopMsgIdx += this.lightbarMsgListNumLines;
else
this.lightbarListTopMsgIdx -= this.lightbarMsgListNumLines;
this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
else
{
// The user is on the first page - Go to the first message on the page.
if (this.lightbarListSelectedMsgIdx != 0)
{
// Print the current message information with regular colors
this.PrintMessageInfo(msgHeader, false, this.lightbarListSelectedMsgIdx+1);
// Go to the first message of the current page
if (this.reverseListOrder)
this.lightbarListSelectedMsgIdx += (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
else
this.lightbarListSelectedMsgIdx -= (this.lightbarListCurPos.y - this.lightbarMsgListStartScreenRow);
// Move the cursor to the first message line
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
}

nightfox
committed
}
// F: First page
else if (userInput == "F")
{
var canGoToFirst = false;
if (this.reverseListOrder)

nightfox
committed
canGoToFirst = (this.lightbarListTopMsgIdx < this.NumMessages() - 1);
else
canGoToFirst = (this.lightbarListTopMsgIdx > 0);
if (canGoToFirst)
{
if (this.reverseListOrder)

nightfox
committed
this.lightbarListTopMsgIdx = this.NumMessages() - 1;
else
this.lightbarListTopMsgIdx = 0;
this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
}
// L: Last page
else if (userInput == "L")
{
if (!lastPage)
{
// Set the top message index. If this.lightbarListTopMsgIdx is beyond the last
// message in the sub-board, then move back a full page of messages.
if (this.reverseListOrder)

nightfox
committed
{
this.lightbarListTopMsgIdx = (this.NumMessages() % this.lightbarMsgListNumLines) - 1;
// If this.lightbarListTopMsgIdx is now invalid (below 0), then adjust it
// to properly display the last page of messages.
if (this.lightbarListTopMsgIdx < 0)
this.lightbarListTopMsgIdx = this.lightbarMsgListNumLines - 1;
}
else
{
var totalNumMessages = this.NumMessages();
this.lightbarListTopMsgIdx = totalNumMessages - (totalNumMessages % this.lightbarMsgListNumLines);
if (this.lightbarListTopMsgIdx >= totalNumMessages)
this.lightbarListTopMsgIdx = totalNumMessages - this.lightbarMsgListNumLines;
}

nightfox
committed
this.lightbarListSelectedMsgIdx = this.lightbarListTopMsgIdx;
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
// If we were on the last page, then clear the screen from
// the current line to the end of the screen.
if (lastPage)
{
this.lightbarListCurPos = console.getxy();
clearToEOS(this.lightbarListCurPos.y);
// Make sure the help line is still there
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}

nightfox
committed
// Move the cursor back to the first message info line
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
}
// Numeric digit: The start of a number of a message to read
else if (userInput.match(/[0-9]/))
{
var originalCurpos = console.getxy();

nightfox
committed
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
// Put the user's input back in the input buffer to
// be used for getting the rest of the message number.
console.ungetstr(userInput);
// Move the cursor to the bottom of the screen and
// prompt the user for the message number.
console.gotoxy(1, console.screen_rows);
userInput = this.PromptForMsgNum({ x: 1, y: console.screen_rows }, this.text.readMsgNumPromptText, true, ERROR_PAUSE_WAIT_MS, false);
if (userInput > 0)
{
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ userInput + this.colors["readMsgConfirmColor"]
+ ": Are you sure";
readMsg = console.yesno(sReadMsgConfirmText);
}
if (readMsg)
{
// Update the message list screen variables
this.CalcMsgListScreenIdxVarsFromMsgNum(+userInput);
// Let the user read the message
retObj.selectedMsgOffset = userInput - 1;

nightfox
committed
if (pReturnOnMsgSelect)
return retObj;
else
{
this.readAMessage = true;
if (this.SearchingAndResultObjsDefinedForCurSub())
this.ReadMessage(this.lightbarListSelectedMsgIdx);

nightfox
committed
else
this.ReadMessage(userInput-1);

nightfox
committed
}
}
else
this.deniedReadingMessage = true;

nightfox
committed
// Prompt the user whether or not to continue listing
// messages.
if (this.promptToContinueListingMessages)
{
continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +
"Continue listing messages");
}
}

nightfox
committed
// If the user chose to continue listing messages, then re-draw
// the screen.
if (continueOn)
{
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(originalCurpos); // Put the cursor back where it should be
}
}
// DEL key: Delete a message
else if (userInput == KEY_DEL)
{
if (this.CanDelete() || this.CanDeleteLastMsg())
{
var originalCurpos = console.getxy();

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

nightfox
committed
// Refresh the screen
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(originalCurpos); // Put the cursor back where it should be
}
}
// E: Edit a message
else if (userInput == "E")
{
if (this.CanEdit())
{
var originalCurpos = console.getxy();

nightfox
committed
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
// Ask the user if they really want to edit the message
console.gotoxy(1, console.screen_rows);
console.print("\1n");
console.clearline();
// Let the user edit the message
//var returnObj = this.EditExistingMsg(msgHeader.offset);
var returnObj = this.EditExistingMsg(this.lightbarListSelectedMsgIdx);
// Refresh the screen
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(originalCurpos); // Put the cursor back where it should be
}
}
// G: Go to a specific message by # (highlight or place that message on the top)
else if (userInput == "G")
{
var originalCurpos = console.getxy();
// Move the cursor to the bottom of the screen and
// prompt the user for a message number.
console.gotoxy(1, console.screen_rows);
userInput = this.PromptForMsgNum({ x: 1, y: console.screen_rows }, "\n" + this.text.goToMsgNumPromptText, true, ERROR_PAUSE_WAIT_MS, false);
if (userInput > 0)
{
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
// Make sure the message number is for a valid message (i.e., it
// could be an invalid message number if there is a search, where
// not all message numbers are consecutive).
if (this.GetMsgHdrByMsgNum(userInput) != null)
{
// If the message is on the current page, then just go to and
// highlight it. Otherwise, set the user's selected message on the
// top of the page. We also have to make sure that this.lightbarListCurPos.y and
// originalCurpos.y are set correctly. Also, account for search
// results if there are any (we'll need to have the correct array
// index for the search results).
var chosenMsgIndex = userInput - 1;
if ((chosenMsgIndex <= bottomMsgIndex) && (chosenMsgIndex >= this.lightbarListTopMsgIdx))
{
this.lightbarListSelectedMsgIdx = chosenMsgIndex;
originalCurpos.y = this.lightbarListCurPos.y = this.lightbarListSelectedMsgIdx - this.lightbarListTopMsgIdx + this.lightbarMsgListStartScreenRow;
}
else
{
this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx = chosenMsgIndex;
originalCurpos.y = this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow;
}
}
else
{
// The user entered an invalid message number
console.print("\1n" + this.text.invalidMsgNumText.replace("%d", userInput) + "\1n");
console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
}

nightfox
committed
}

nightfox
committed
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
// Clear & re-draw the screen, to fix any possible alignment problems
// caused by newline output after the user inputs their choice.
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
console.gotoxy(originalCurpos); // Put the cursor back where it should be
}
// C: Change to another message area (sub-board)
else if (userInput == "C")
{
if (allowChgSubBoard && (this.subBoardCode != "mail"))
{
// Store the current sub-board code so we can see if it changed
var oldSubCode = bbs.cursub_code;
// Let the user choose another message area. If they chose
// a different message area, then set up the message base
// object accordingly.
this.SelectMsgArea();
if (bbs.cursub_code != oldSubCode)
{
var chgSubRetval = this.ChangeSubBoard(bbs.cursub_code);
continueOn = chgSubRetval.succeeded;
}
// Update the lightbar list variables and refresh the screen
if (continueOn)
{
this.SetUpLightbarMsgListVars();
console.clear("\1n");
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
// List a screenful of message headers
console.gotoxy(1, this.lightbarMsgListStartScreenRow);
var lastPage = this.ListScreenfulOfMessages(this.lightbarListTopMsgIdx, this.lightbarMsgListNumLines);
// Move the cursor to where it needs to be
console.gotoxy(this.lightbarListCurPos);
}
}
}
}

nightfox
committed
return retObj;
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
}
// For the DigDistMsgListerClass: Prints a line of information about
// a message.
//
// Parameters:
// pMsgHeader: The message header object, returned by MsgBase.get_msg_header().
// pHighlight: Optional boolean - Whether or not to highlight the line or
// use the standard colors.
// pMsgNum: Optional - A number to use for the message instead of the number/offset
// in the message header
function DigDistMsgReader_PrintMessageInfo(pMsgHeader, pHighlight, pMsgNum)
{
// pMsgHeader must be a valid object.
if (typeof(pMsgHeader) == "undefined")
return;
if (pMsgHeader == null)
return;
var highlight = false;
if (typeof(pHighlight) != "undefined")
highlight = pHighlight;
// Determine if the message has been deleted.
var msgDeleted = ((pMsgHeader.attr & MSG_DELETE) == MSG_DELETE);
// Get the message's import date & time as strings. If
// this.msgList_displayMessageDateImported is true, use the message import date.
// Otherwise, use the message written date.
var sDate;
var sTime;
if (this.msgList_displayMessageDateImported)
{
sDate = strftime("%Y-%m-%d", pMsgHeader.when_imported_time);
sTime = strftime("%H:%M:%S", pMsgHeader.when_imported_time);
}
else
{
sDate = strftime("%Y-%m-%d", pMsgHeader.when_written_time);
sTime = strftime("%H:%M:%S", pMsgHeader.when_written_time);
}
// Write the message header information.
// Note: The message header has the following fields:
// 'number': The message number
// 'offset': The message offset
// 'to': Who the message is directed to (string)
// 'from' Who wrote the message (string)
// 'subject': The message subject (string)
// 'date': The date - Full text (string)
// To access one of these, use brackets; i.e., msgHeader['to']
var msgNum = (typeof(pMsgNum) == "number" ? pMsgNum : pMsgHeader.offset+1);
if (highlight)
{
printf(this.sMsgInfoFormatHighlightStr,
msgNum,
(msgDeleted ? "\1r\1i*\1n\1h" + this.colors["msgListHighlightBkgColor"] : " "),
pMsgHeader.from.substr(0, this.FROM_LEN),
pMsgHeader.to.substr(0, this.TO_LEN),
pMsgHeader.subject.substr(0, this.SUBJ_LEN),
sDate, sTime);
}
else
{
// Determine whether to use the normal, "to-user", or "from-user" format string.
// The differences are the colors. Then, output the message information line.
var toNameUpper = pMsgHeader.to.toUpperCase();
var msgToUser = ((toNameUpper == user.alias.toUpperCase()) || (toNameUpper == user.name.toUpperCase()) || (toNameUpper == user.handle.toUpperCase()));
var fromNameUpper = pMsgHeader.from.toUpperCase();
var msgIsFromUser = ((fromNameUpper == user.alias.toUpperCase()) || (fromNameUpper == user.name.toUpperCase()) || (fromNameUpper == user.handle.toUpperCase()));
printf((msgToUser ? this.sMsgInfoToUserFormatStr :
(msgIsFromUser ? this.sMsgInfoFromUserFormatStr :
this.sMsgInfoFormatStr)),
msgNum,
(msgDeleted ? "\1r\1i*\1n" : " "),
pMsgHeader.from.substr(0, this.FROM_LEN),
pMsgHeader.to.substr(0, this.TO_LEN),
pMsgHeader.subject.substr(0, this.SUBJ_LEN),
sDate, sTime);
}
console.cleartoeol("\1"); // To clear away any extra text that may have been entered by the user
}
// For the traditional interface of DigDistMsgListerClass: Prompts the user to
// continue or read a message (by number).
//
// Parameters:
// pStart: Whether or not we're on the first page (true or false)
// pEnd: Whether or not we're at the last page (true or false)
// pReturnOnMsgSelect: Optional - A boolean to specify whether or not
// to return when a message is selected to read.

nightfox
committed
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
//
// Return value: An object with the following properties:
// continueOn: Boolean, whether or not the user wants to continue
// listing the messages
// userInput: The user's input
// selectedMsgOffset: The offset of the message selected to read,
// if one was selected. If a message was not
// selected, this will be -1.

nightfox
committed
function DigDistMsgReader_PromptContinueOrReadMsg(pStart, pEnd, pReturnOnMsgSelect, pAllowChgSubBoard)
{

nightfox
committed
// Create the return object and set some initial default values
var retObj = new Object();
retObj.continueOn = true;
retObj.userInput = "";
retObj.selectedMsgOffset = -1;
var allowChgSubBoard = (typeof(pAllowChgSubBoard) == "boolean" ? pAllowChgSubBoard : true);
var continueOn = true;
// Prompt the user whether or not to continue or to read a message
// (by message number). Make use of the different prompt texts,
// depending whether we're at the beginning, in the middle, or at
// the end of the message list.
var userInput = "";
var allowedKeys = "?G"; // ? = help, G = Go to message #

nightfox
committed
if (allowChgSubBoard)
allowedKeys += "C"; // Change to another message area
if (this.CanDelete() || this.CanDeleteLastMsg())

nightfox
committed
allowedKeys += "D"; // Delete
if (this.CanEdit())
allowedKeys += "E"; // Edit
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
if (pStart && pEnd)
{
// This is the only page.
console.print(this.msgListOnlyOnePageContinuePrompt);
// Get input from the user. Allow only Q (quit).
allowedKeys += "Q";
}
else if (pStart)
{
// We're on the first page.
console.print(this.sStartContinuePrompt);
// Get input from the user. Allow only L (last), N (next), or Q (quit).
allowedKeys += "LNQ";
}
else if (pEnd)
{
// We're on the last page.
console.print(this.sEndContinuePrompt);
// Get input from the user. Allow only F (first), P (previous), or Q (quit).
allowedKeys += "FPQ";
}
else
{
// We're neither on the first nor last page. Allow F (first), L (last),
// N (next), P (previous), or Q (quit).
console.print(this.sContinuePrompt);
allowedKeys += "FLNPQ";
}
// Get the user's input. Allow the keys in allowedKeys or a number from 1
// to the highest message number.
userInput = console.getkeys(allowedKeys, this.HighestMessageNum()).toString();
if (userInput == "Q")
continueOn = false;
// If the user has typed all numbers, then read that message.
if ((userInput != "") && /^[0-9]+$/.test(userInput))
{
// If the user entered a valid message number, then let the user read the message.
// The message number might be invalid if there are search results that
// have non-continuous message numbers.
if (this.IsValidMessageNum(userInput))
{
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]

nightfox
committed
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ userInput + this.colors["readMsgConfirmColor"]
+ ": Are you sure";
readMsg = console.yesno(sReadMsgConfirmText);
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
}
if (readMsg)
{
// Update the message list screen variables
this.CalcMsgListScreenIdxVarsFromMsgNum(+userInput);
// Let the user read the message
if (pReturnOnMsgSelect)
{
// Fill a return object with the required values, and return it.
retObj.continueOn = continueOn;
retObj.userInput = userInput;
retObj.selectedMsgOffset = userInput-1;
return retObj;
}
else
{
this.readAMessage = true;
this.ReadMessage(userInput-1);
}
}
else
this.deniedReadingMessage = true;
// Prompt the user whether or not to continue listing
// messages.
if (this.promptToContinueListingMessages)
{
continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +

nightfox
committed
"Continue listing messages");
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
}
}
else
{
// The user entered an invalid message number.
console.print("\1n\1h\1w" + userInput + " \1y is not a valid message number.\1n");
console.crlf();
console.pause();
continueOn = true;
}
}
// Make sure color highlighting is turned off
console.print("\1n");
// Fill the return object with the required values, and return it.
retObj.continueOn = continueOn;
retObj.userInput = userInput;
return retObj;
}
// For the DigDistMsgReader Class: Given a message number of a message in the
// current message area, shows the message to the user and allows the user to
// respond.
//
// Parameters:
// pOffset: The offset of the message to be read
//
// Return value: And object with the following properties:
// offsetValid: Boolean - Whether or not the passed-in offset was valid
// userReplied: Boolean - Whether or not the user replied to the message.
// msgbaseReOpened: Boolean - Whether or not the messagebase is open after
// the user replied to the message. Will be true if
// the user didn't reply to the message.
function DigDistMsgReader_ReadMessage(pOffset)
{
var retObj = new Object();
retObj.offsetValid = true;
retObj.userReplied = false;
retObj.msgbaseReOpened = true;
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
// Get the message header
var msgHeader = this.GetMsgHdrByMsgNum(pOffset+1);
if (msgHeader == null)
{
console.print("\1n" + this.text.invalidMsgNumText.replace("%d", +(pOffset+1)) + "\1n");
console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
retObj.offsetValid = false;
return retObj;
}
// Show the message header.
this.DisplaySyncMsgHeader(msgHeader);
// Show the message body. Make sure the text is word-wrapped
// so that it looks good.
var msgText = this.msgbase.get_msg_body(true, msgHeader.offset);
var msgTextWrapped = word_wrap(msgText, console.screen_columns-1);
console.print("\1n" + this.colors["msgBodyColor"]);
console.putmsg(msgTextWrapped, P_NOATCODES);
// Hack: If the "from" name in the header is blank (as it might be sometimes), then
// set it to "All". This prevents Synchronet from crashing, and it will also default
// the "to" name in the user's reply to "All".
if (msgHeader["from"] == "")
msgHeader["from"] = "All";
// Mark the message as read, if it was written to the current
// user.
var msgToUpper = msgHeader["to"].toUpperCase();
if ((msgToUpper == user.alias.toUpperCase()) || (msgToUpper == user.name.toUpperCase()))
{
msgHeader.attr = (msgHeader.attr | MSG_READ);
var wroteHeader = this.msgbase.put_msg_header(true, msgHeader.offset, msgHeader);
}
// If not reading personal email, then update the scan & last read message pointers.
if (this.subBoardCode != "mail") // && !this.SearchTypePopulatesSearchResults()
{
if (msgHeader.number > msg_area.sub[this.subBoardCode].scan_ptr)
msg_area.sub[this.subBoardCode].scan_ptr = msgHeader.number;
msg_area.sub[this.subBoardCode].last_read = msgHeader.number;
}
// Allow the user to reply to the message, either publicly or privately.
console.print("\1n\1cEnd of message. \1hR\1b)\1n\1ceply\1h\1b, " +
"\1cP\1b)\1n\1crivate reply\1h\1b, \1cENTER\1b/\1cN\1b)\1n\1co reply\1h\1g: \1n\1c");
var userKey = console.getkeys("RPN").toString();
var privateReply = (userKey == "P");
if ((userKey == "R") || privateReply)
{
var replyRetObj = this.ReplyToMsg(msgHeader, msgText, privateReply, pOffset);
retObj.userReplied = replyRetObj.postSucceeded;
retObj.msgbaseReOpened = replyRetObj.msgbaseReOpened;
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
}
return retObj;
}
// For the DigDistMsgReader Class: Given a message number of a message in the
// current message area, shows the message to the user and allows the user to
// respond. This is an enhanced version that allows scrolling up & down the
// message with the up & down arrow keys, and the left & right arrow keys will
// return from the function to allow calling code to navigate back & forth
// through the message sub-board.
//
// Parameters:
// pOffset: The offset of the message to be read
// pAllowChgArea: Optional boolean - Whether or not to allow changing the
// message area
//
// Return value: And object with the following properties:
// offsetValid: Boolean - Whether or not the passed-in offset was valid
// msgDeleted: Boolean - Whether or not the message is marked as deleted
// (not deleted by the user in the reader)
// userReplied: Boolean - Whether or not the user replied to the message.
// lastKeypress: The last keypress from the user - For navigation purposes
// newMsgOffset: The offset of another message to read, if the user
// input another message number. If the user did not
// input another message number, this will be -1.
// nextAction: The next action for the caller to take. This will be
// one of the values specified by the ACTION_* constants.
// This defaults to ACTION_NONE on error.
// refreshEnhancedRdrHelpLine: Boolean - Whether or not to refresh the
// enhanced reader help line on the screen
// (for instance, if switched to the traditional
// non-scrolling interface to read the message)
function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea)
{
var retObj = new Object();
retObj.offsetValid = true;
retObj.msgDeleted = false;
retObj.userReplied = false;
retObj.lastKeypress = "";
retObj.newMsgOffset = -1;
retObj.nextAction = ACTION_NONE;
retObj.refreshEnhancedRdrHelpLine = false;
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
// Get the message header
var msgHeader = this.GetMsgHdrByIdx(pOffset);
if (msgHeader == null)
{
console.print("\1n" + this.text.invalidMsgNumText.replace("%d", +(pOffset+1)) + "\1n");
console.crlf();
console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
retObj.offsetValid = false;
return retObj;
}
// See if the message is marked as deleted. If so, don't let the
// user read it, just silently return.
retObj.msgDeleted = ((msgHeader.attr & MSG_DELETE) == MSG_DELETE);
if (retObj.msgDeleted)
return retObj;
// Update the message list index variables so that the message list is in
// the right spot for the message currently being read
this.CalcMsgListScreenIdxVarsFromMsgNum(pOffset+1);
// Check the pAllowChgArea parameter. If it's a boolean, then use it. If
// not, then check to see if we're reading personal mail - If not, then allow
// the user to change to a different message area.
var allowChgMsgArea = true;
if (typeof(pAllowChgArea) == "boolean")
allowChgMsgArea = pAllowChgArea;
else
allowChgMsgArea = (this.subBoardCode != "mail");
// Hack: If the "from" name in the header is empty (as it might be sometimes), then
// set it to "All". This prevents Synchronet from crashing, and it will also default
// the "to" name in the user's reply to "All".
if (msgHeader.from.length == 0)
msgHeader.from = "All";

nightfox
committed
// Some key bindings
var enhReaderKeys = new Object();
enhReaderKeys.prevMsgByTitle = "<";
enhReaderKeys.nextMsgByTitle = ">";
enhReaderKeys.prevMsgByAuthor = "{";
enhReaderKeys.nextMsgByAuthor = "}";
enhReaderKeys.prevMsgByToUser = "[";
enhReaderKeys.nextMsgByToUser = "]";
enhReaderKeys.prevMsgByThreadID = "(";
enhReaderKeys.nextMsgByThreadID = ")";

nightfox
committed
enhReaderKeys.prevSubBoard = "-";
enhReaderKeys.nextSubBoard = "+";
enhReaderKeys.downloadAttachments = CTRL_D;
enhReaderKeys.saveToBBSMachine = CTRL_S;

nightfox
committed
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
// This function converts a thread navigation key character to its
// corresponding thread type value
function keypressToThreadType(pKeypress)
{
var threadType = THREAD_BY_ID;
switch (pKeypress)
{
case enhReaderKeys.prevMsgByTitle:
case enhReaderKeys.nextMsgByTitle:
threadType = THREAD_BY_TITLE;
break;
case enhReaderKeys.prevMsgByAuthor:
case enhReaderKeys.nextMsgByAuthor:
threadType = THREAD_BY_AUTHOR;
break;
case enhReaderKeys.prevMsgByToUser:
case enhReaderKeys.nextMsgByToUser:
threadType = THREAD_BY_TO_USER;
break;
case enhReaderKeys.prevMsgByThreadID:
case enhReaderKeys.nextMsgByThreadID:
default:
threadType = THREAD_BY_ID;
break;
}
return threadType;
}
// Get the message text and see if it has any ANSI codes. If it has ANSI codes,
// then don't use the scrolling interface so that the ANSI gets displayed properly.
var messageText = this.msgbase.get_msg_body(true, msgHeader.offset);
// If the message has ANSI content, then use the scrolling interface only
// if frame.js is available on the BBS machine and the option to use the
// scrolling interface for ANSI messages is enabled.
var msgHasANSICodes = textHasANSICodes(messageText);
var useScrollingInterface = this.scrollingReaderInterface && console.term_supports(USER_ANSI);
if (useScrollingInterface && msgHasANSICodes)
useScrollingInterface = gFrameJSAvailable && this.useScrollingInterfaceForANSIMessages;
// If we switch to the non-scrolling interface here, then the calling method should
// refresh the enhanced reader help line on the screen.
retObj.refreshEnhancedRdrHelpLine = (this.scrollingReaderInterface && !useScrollingInterface);
// Use the scrollable reader interface if the setting is enabled & the user's
// terminal supports ANSI. Otherwise, use a more traditional user interface.
if (useScrollingInterface)
{
// Show the message header
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
// Get the message text, interpret any @-codes in it, replace tabs with spaces
// to prevent weirdness when displaying the message lines, and word-wrap the
// text so that it looks good on the screen,
var msgInfo = this.GetMsgInfoForEnhancedReader(msgHeader, true, true, true, messageText, msgHasANSICodes);
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
var topMsgLineIdxForLastPage = msgInfo.topMsgLineIdxForLastPage;
var msgFractionShown = msgInfo.msgFractionShown;
var numSolidScrollBlocks = msgInfo.numSolidScrollBlocks;
var numNonSolidScrollBlocks = msgInfo.numNonSolidScrollBlocks;
var solidBlockStartRow = msgInfo.solidBlockStartRow;
var solidBlockLastStartRow = solidBlockStartRow;
var topMsgLineIdx = 0;
var fractionToLastPage = 0;
if (topMsgLineIdxForLastPage != 0)
fractionToLastPage = topMsgLineIdx / topMsgLineIdxForLastPage;
// Draw an initial scrollbar on the rightmost column of the message area
// showing the fraction of the message shown and what part of the message
// is currently being shown. The scrollbar will be updated minimally in
// the input loop to minimize screen redraws.
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
// Input loop (for scrolling the message up & down)
var msgLineFormatStr = "%-" + this.msgAreaWidth + "s";
var writeMessage = true;
// msgAreaHeight, msgReaderObj, and scrollbarUpdateFunction are for use
// with scrollTextLines().
var msgAreaHeight = this.msgAreaBottom - this.msgAreaTop + 1;
var msgReaderObj = this;
function msgScrollbarUpdateFn(pFractionToLastPage)
{
// Update the scrollbar position for the message, depending on the
// value of pFractionToLastMessage.
fractionToLastPage = pFractionToLastPage;
solidBlockStartRow = msgReaderObj.msgAreaTop + Math.floor(numNonSolidScrollBlocks * pFractionToLastPage);
if (solidBlockStartRow != solidBlockLastStartRow)
msgReaderObj.UpdateEnhancedReaderScollbar(solidBlockStartRow, solidBlockLastStartRow, numSolidScrollBlocks);
solidBlockLastStartRow = solidBlockStartRow;
console.gotoxy(1, console.screen_rows);
}
// User input loop
var continueOn = true;
while (continueOn)
{
// Display the message lines (depending on the value of writeMessage)
// and handle scroll keys via scrollTextLines(). Handle other keypresses
// here.
var scrollRetObj = null;
if (msgInfo.displayFrame != null)
{
msgInfo.displayFrame.draw();
scrollRetObj = scrollFrame(msgInfo.displayFrame, msgInfo.displayFrameScrollbar,
topMsgLineIdx, this.colors["msgBodyColor"],
writeMessage, 1, console.screen_rows,
msgScrollbarUpdateFn);
}
else
{
scrollRetObj = scrollTextLines(msgInfo.messageLines, topMsgLineIdx,
this.colors["msgBodyColor"], writeMessage,
this.msgAreaLeft, this.msgAreaTop, this.msgAreaWidth,
msgAreaHeight, 1, console.screen_rows,
msgScrollbarUpdateFn);
}
topMsgLineIdx = scrollRetObj.topLineIdx;
retObj.lastKeypress = scrollRetObj.lastKeypress;
switch (retObj.lastKeypress)
{
case KEY_DEL: // Delete message
var originalCurpos = console.getxy();
// The 2nd to last row of the screen is where the user will
// be prompted for confirmation to delete the message.
// Ideally, I'd like to put the cursor on the last row of
// the screen for this, but console.noyes() lets the enter
// key shift everything on screen up one row, and there's
// no way to avoid that. So, to optimize screen refreshing,
// the cursor is placed on the 2nd to the last row on the
// screen to prompt for confirmation.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
// Prompt the user for confirmation to delete the message.
// Note: this.DeleteMessage() will check to see if the user
// is a sysop or the message was posted by the user.
// If the message was deleted, then exit this read method
// and return KEY_RIGHT as the last keypress so that the
// calling method will go to the next message/sub-board.
// Otherwise (if the message was not deleted), refresh the
// last 2 lines of the message on the screen.
var msgWasDeleted = this.DeleteMessage(pOffset, promptPos, true, this.msgAreaWidth,
true, msgInfo.attachments);
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
if (msgWasDeleted)
{
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
continueOn = msgSearchObj.continueInputLoop;
retObj.newMsgOffset = msgSearchObj.newMsgOffset;
retObj.nextAction = msgSearchObj.nextAction;
if (msgSearchObj.promptGoToNextArea)
{
if (this.EnhReaderPromptYesNo(this.text.goToNextMsgAreaPromptText, msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = false; // No need to refresh the message
}
}
else
{
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
// Move the cursor back to its original position
console.gotoxy(originalCurpos);
writeMessage = false;
}
break;
case "E": // Edit the messaage
if (this.CanEdit())
{
// Move the cursor to the last line in the message area so
// the edit confirmation prompt will appear there. Not using
// the last line on the screen because the yes/no prompt will
// output a carriage return and move everything on the screen
// up one line, which is not ideal in case the user says No.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
// Let the user edit the message if they want to
var editReturnObj = this.EditExistingMsg(pOffset);
// If the user didn't confirm, then we only have to refresh the bottom
// help line. Otherwise, we need to refresh everything on the screen.
if (!editReturnObj.userConfirmed)
{
// For some reason, the yes/no prompt erases the last character
// of the scrollbar - So, figure out which block was there and
// refresh it.
//var scrollBarBlock = "\1n\1h\1k" + BLOCK1; // Dim block
// Dim block
var scrollBarBlock = this.colors.scrollbarBGColor + this.text.scrollbarBGChar;
if (solidBlockStartRow + numSolidScrollBlocks - 1 == this.msgAreaBottom)
{
//scrollBarBlock = "\1w" + BLOCK2; // Bright block
// Bright block
scrollBarBlock = this.colors.scrollbarScrollBlockColor + this.text.scrollbarScrollBlockChar;
}
console.gotoxy(this.msgAreaRight+1, this.msgAreaBottom);
console.print(scrollBarBlock);
// Refresh the last 2 message lines on the screen, then display
// the key help line
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
writeMessage = false;
}
else
{
// If the message was edited, then refresh the text lines
// array and update the other message-related variables.
if (editReturnObj.msgEdited && (editReturnObj.newMsgIdx > -1))
{
// When the message is edited, the old message will be
// deleted and the edited message will be posted as a new
// message. So we should return to the caller and have it
// go directly to that new message.
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
continueOn = false;
retObj.newMsgOffset = editReturnObj.newMsgIdx;
}
else
{
// The message was not edited. Refresh everything on the screen.
// If the enhanced message header width is less than the console
// width, then clear the screen to remove anything that might be
// left on the screen by the message editor.
if (this.enhMsgHeaderWidth < console.screen_columns)
console.clear("\1n");
// Display the message header and key help line
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again, and ensure it's in the correct position
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
writeMessage = true; // We want to refresh the message on the screen
}
}
}
else
writeMessage = false; // Don't write the current message again
break;
case "?": // Show the help screen
this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgInfo.attachments.length > 0);
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
// If the enhanced message header width is less than the console
// width, then clear the screen to remove anything left on the
// screen from the help screen.
if (this.enhMsgHeaderWidth < console.screen_columns)
console.clear("\1n");
// Display the message header and key help line
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again, and ensure it's in the correct position
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
writeMessage = true; // We want to refresh the message on the screen
break;
case "R": // Reply to the message
case "I": // Private message reply
// If the user pressed P (private reply) while reading private
// mail, then do nothing (allow only the "R" key to reply).
var privateReply = (retObj.lastKeypress == "I");
if (privateReply && this.readingPersonalEmail)
writeMessage = false; // Don't re-write the current message again
else
{
// Let the user reply to the message.
var replyRetObj = this.ReplyToMsg(msgHeader, msgInfo.msgText, privateReply, pOffset);
retObj.userReplied = replyRetObj.postSucceeded;
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
//retObj.msgDeleted = replyRetObj.msgWasDeleted;
var msgWasDeleted = replyRetObj.msgWasDeleted;
//if (retObj.msgDeleted)
if (msgWasDeleted)
{
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
continueOn = msgSearchObj.continueInputLoop;
retObj.newMsgOffset = msgSearchObj.newMsgOffset;
retObj.nextAction = msgSearchObj.nextAction;
if (msgSearchObj.promptGoToNextArea)
{
if (this.EnhReaderPromptYesNo(this.text.goToNextMsgAreaPromptText, msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = true; // We want to refresh the message on the screen
}
}
else
{
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
// If the messagebase object was successfully re-opened after
// posting the message, then refresh the screen. Otherwise,
// we'll want to quit.
if (replyRetObj.msgbaseReOpened)
{
// If the enhanced message header width is less than the console
// width, then clear the screen to remove anything left on the
// screen by the message editor.
if (this.enhMsgHeaderWidth < console.screen_columns)
console.clear("\1n");
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
writeMessage = true; // We want to refresh the message on the screen
}
else
{
retObj.nextAction = ACTION_QUIT;
continueOn = false;
// Display an error
console.print("\1n");
console.crlf();
console.print("\1h\1yMessagebase error after replying. Aborting.\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
}
}
break;
case "P": // Post a message
if (!this.readingPersonalEmail)
{
// Let the user post a message.
if (bbs.post_msg(this.subBoardCode))
{
if (searchTypePopulatesSearchResults(this.searchType))
{
// TODO: If the user is doing a search, it might be
// useful to search their new message and add it to
// the search results if it's a match.. but maybe
// not?
}
console.pause();
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
}
// Refresh things on the screen
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
writeMessage = true; // We want to refresh the message on the screen
}
else
writeMessage = false; // Don't re-write the current message again
break;
// Numeric digit: The start of a number of a message to read
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
var originalCurpos = console.getxy();
// Put the user's input back in the input buffer to
// be used for getting the rest of the message number.
console.ungetstr(retObj.lastKeypress);
// Move the cursor to the 2nd to last row of the screen and
// prompt the user for the message number. Ideally, I'd like
// to put the cursor on the last row of the screen for this, but
// console.getnum() lets the enter key shift everything on screen
// up one row, and there's no way to avoid that. So, to optimize
// screen refreshing, the cursor is placed on the 2nd to the last
// row on the screen to prompt for the message number.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
// Prompt for the message number
var msgNumInput = this.PromptForMsgNum(promptPos, this.text.readMsgNumPromptText, false, ERROR_PAUSE_WAIT_MS, false);
// Only allow reading the message if the message number is valid
// and it's not the same message number that was passed in.
if ((msgNumInput > 0) && (msgNumInput-1 != pOffset))
{
// If the message is marked as deleted, then output an error
if (this.MessageIsDeleted(msgNumInput-1))
{
writeWithPause(this.msgAreaLeft, console.screen_rows-1,
"\1n" + this.text.msgHasBeenDeletedText.replace("%d", msgNumInput) + "\1n",
ERROR_PAUSE_WAIT_MS, "\1n", true);
}
else
{
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ msgNumInput + this.colors["readMsgConfirmColor"]
+ ": Are you sure";
console.gotoxy(promptPos);
console.print("\1n");
readMsg = console.yesno(sReadMsgConfirmText);
}
if (readMsg)
{
continueOn = false;
retObj.newMsgOffset = msgNumInput - 1;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
}
else
writeMessage = false; // Don't re-write the current message again
}
}
else // Message number invalid or the same as what was passed in
writeMessage = false; // Don't re-write the current message again

nightfox
committed
// If the user chose to continue reading messages, then refresh
// the last 2 message lines in the last part of the message area
// and then put the cursor back to its original position.
if (continueOn)
{
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
// Move the cursor back to its original position
console.gotoxy(originalCurpos);
}
break;

nightfox
committed
case enhReaderKeys.prevMsgByTitle: // Previous message by title
case enhReaderKeys.prevMsgByAuthor: // Previous message by author
case enhReaderKeys.prevMsgByToUser: // Previous message by 'to user'
case enhReaderKeys.prevMsgByThreadID: // Previous message by thread ID
// Only allow this if we aren't doing a message search.
if (!this.SearchingAndResultObjsDefinedForCurSub())
{

nightfox
committed
var threadPrevMsgOffset = this.FindThreadPrevOffset(msgHeader,
keypressToThreadType(retObj.lastKeypress),
true);
if (threadPrevMsgOffset > -1)
{
retObj.newMsgOffset = threadPrevMsgOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
}
else
{
// Refresh the help line at the bottom of the screen
//this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
writeMessage = false; // Don't re-write the current message again
}
// Make sure the help line on the bottom of the screen is
// drawn.
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
writeMessage = false; // Don't re-write the current message again
break;

nightfox
committed
case enhReaderKeys.nextMsgByTitle: // Next message by title (subject)
case enhReaderKeys.nextMsgByAuthor: // Next message by author
case enhReaderKeys.nextMsgByToUser: // Next message by 'to user'
case enhReaderKeys.nextMsgByThreadID: // Next message by thread ID
// Only allow this if we aren't doing a message search.
if (!this.SearchingAndResultObjsDefinedForCurSub())
{

nightfox
committed
var threadPrevMsgOffset = this.FindThreadNextOffset(msgHeader,
keypressToThreadType(retObj.lastKeypress),
true);
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
if (threadPrevMsgOffset > -1)
{
retObj.newMsgOffset = threadPrevMsgOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
}
else
writeMessage = false; // Don't re-write the current message again
// Make sure the help line on the bottom of the screen is
// drawn.
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
writeMessage = false; // Don't re-write the current message again
break;
case KEY_LEFT: // Previous message
// Look for a prior message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the previous message sub-board/group.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, false);
// As a screen redraw optimization: Only return if there is a valid new
// message offset or the user is allowed to change to a different sub-board.
// Otherwise, don't return, and don't refresh the message on the screen.
var goToPrevMessage = false;
if ((retObj.newMsgOffset > -1) || allowChgMsgArea)
{
if (retObj.newMsgOffset == -1 && !curMsgSubBoardIsLast())
{
goToPrevMessage = this.EnhReaderPromptYesNo(this.text.goToPrevMsgAreaPromptText,
msgInfo.messageLines, topMsgLineIdx,
msgLineFormatStr, solidBlockStartRow,
numSolidScrollBlocks);
}
else
{
// We're not at the beginning of the sub-board, so it's okay to exit this
// method and go to the previous message.
goToPrevMessage = true;
}
}
if (goToPrevMessage)
{
continueOn = false;
retObj.nextAction = ACTION_GO_PREVIOUS_MSG;
}
else
writeMessage = false; // No need to refresh the message
break;
case KEY_RIGHT: // Next message
case KEY_ENTER:
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
// Look for a later message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the next message sub-board/group.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, true);
// Note: Unlike the left arrow key, we want to exit this method when
// navigating to the next message, regardless of whether or not the
// user is allowed to change to a different sub-board, so that processes
// that require continuation (such as new message scan) can continue.
// Still, if there are no more readable messages in the current sub-board
// (and thus the user would go onto the next message area), prompt the
// user whether they want to continue onto the next message area.
if (retObj.newMsgOffset == -1 && !curMsgSubBoardIsLast())
{
// For personal mail, don't do anything, and don't refresh the
// message. In a sub-board, ask the user if they want to go
// to the next one.
if (this.readingPersonalEmail)
writeMessage = false;
else
{
if (this.EnhReaderPromptYesNo(this.text.goToNextMsgAreaPromptText, msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = false; // No need to refresh the message
}
}
else
{
// We're not at the end of the sub-board, so it's okay to exit this
// method and go to the next message.
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
break;
// First & last message: Quit out of this input loop and let the
// calling function, this.ReadMessages(), handle the action.
case "F": // First message
// Only leave this function if we aren't already on the first message.
if (pOffset > 0)
{
continueOn = false;
retObj.nextAction = ACTION_GO_FIRST_MSG;
}
else
writeMessage = false; // Don't re-write the current message again
break;
case "L": // Last message
// Only leave this function if we aren't already on the last message.
if (pOffset < this.NumMessages() - 1)
{
continueOn = false;
retObj.nextAction = ACTION_GO_LAST_MSG;
}
else
writeMessage = false; // Don't re-write the current message again
break;

nightfox
committed
case enhReaderKeys.prevSubBoard: // Go to the previous message area
if (allowChgMsgArea)
{
continueOn = false;
retObj.nextAction = ACTION_GO_PREV_MSG_AREA;
}
else
writeMessage = false; // Don't re-write the current message again
break;

nightfox
committed
case enhReaderKeys.nextSubBoard: // Go to the next message area
if (allowChgMsgArea || this.doingMultiSubBoardScan)
{
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
}
else
writeMessage = false; // Don't re-write the current message again
break;
// H and K: Display the extended message header info/kludge lines
// (for the sysop)
case "H":
case "K":
if (gIsSysop)
{
// Save the original cursor position
var originalCurPos = console.getxy();
// Get an array of the extended header info/kludge lines and then
// allow the user to scroll through them.
var extdHdrInfoLines = this.GetExtdMsgHdrInfo(msgHeader, (retObj.lastKeypress == "K"));
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
if (extdHdrInfoLines.length > 0)
{
// Calculate information for the scrollbar for the kludge lines
var infoFractionShown = this.msgAreaHeight / extdHdrInfoLines.length;
if (infoFractionShown > 1)
infoFractionShown = 1.0;
var numInfoSolidScrollBlocks = Math.floor(this.msgAreaHeight * infoFractionShown);
if (numInfoSolidScrollBlocks == 0)
numInfoSolidScrollBlocks = 1;
var numNonSolidInfoScrollBlocks = this.msgAreaHeight - numInfoSolidScrollBlocks;
var lastInfoSolidBlockStartRow = this.msgAreaTop;
// Define a scrollbar update function for the header info/kludge lines
function msgInfoScrollbarUpdateFn(pFractionToLastPage)
{
var infoSolidBlockStartRow = msgReaderObj.msgAreaTop + Math.floor(numNonSolidInfoScrollBlocks * pFractionToLastPage);
if (infoSolidBlockStartRow != lastInfoSolidBlockStartRow)
msgReaderObj.UpdateEnhancedReaderScollbar(infoSolidBlockStartRow, lastInfoSolidBlockStartRow, numInfoSolidScrollBlocks);
lastInfoSolidBlockStartRow = infoSolidBlockStartRow;
console.gotoxy(1, console.screen_rows);
}
// Display the kludge lines and let the user scroll through them
this.DisplayEnhancedReaderWholeScrollbar(this.msgAreaTop, numInfoSolidScrollBlocks);
scrollTextLines(extdHdrInfoLines, 0, this.colors["msgBodyColor"], true,
this.msgAreaLeft, this.msgAreaTop, this.msgAreaWidth,
msgAreaHeight, 1, console.screen_rows,
msgInfoScrollbarUpdateFn);
// Display the scrollbar for the message to refresh it on the screen
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
writeMessage = true; // We want to refresh the message on the screen
}
else
{
// There are no kludge lines for this message
this.DisplayEnhReaderError(this.text.noKludgeLinesForThisMsgText, msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
console.gotoxy(originalCurPos);
writeMessage = false;
}
}
else // The user is not a sysop
writeMessage = false;
break;
// Message list, change message area: Quit out of this input loop
// and let the calling function, this.ReadMessages(), handle the
// action.
case "M": // Message list
retObj.nextAction = ACTION_DISPLAY_MSG_LIST;
continueOn = false;
break;
case "C": // Change message area, if allowed
if (allowChgMsgArea)
{
retObj.nextAction = ACTION_CHG_MSG_AREA;
continueOn = false;
}
else
writeMessage = false; // No need to refresh the message
break;
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
case enhReaderKeys.downloadAttachments: // Download attachments
if (msgInfo.attachments.length > 0)
{
console.print("\1n");
console.gotoxy(1, console.screen_rows);
console.crlf();
console.print("\1c- Download Attached Files -\1n");
// Note: sendAttachedFiles() will output a CRLF at the beginning.
sendAttachedFiles(msgInfo.attachments);
// Refresh things on the screen
console.clear("\1n");
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
writeMessage = true; // We want to refresh the message on the screen
}
else
writeMessage = false;
break;
case enhReaderKeys.saveToBBSMachine:
// Save the message to the BBS machine - Only allow this
// if the user is a sysop.
if (gIsSysop)
{
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
// Prompt the user for a filename to save the message to the
// BBS machine
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
console.print("\1n\1cFilename:\1h");
var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1
var filename = console.getstr(inputLen, K_NOCRLF);
console.print("\1n");
if (filename.length > 0)
{
//var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true, msgInfo.messageLines);
var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true);
console.gotoxy(promptPos);
console.cleartoeol("\1n");
console.gotoxy(promptPos);
if (saveMsgRetObj.succeeded)
console.print("\1n\1cThe message has been saved.\1n");
else
console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
else
{
console.gotoxy(promptPos);
console.print("\1n\1y\1hMessage not exported\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
// Refresh the last 2 lines of the message on the screen to overwrite
// the file save prompt
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
writeMessage = false; // Don't write the whole message again
}
else
writeMessage = false;
break;
case "Q": // Quit
retObj.nextAction = ACTION_QUIT;
continueOn = false;
break;
default:
writeMessage = false;
break;
}
}
}
else
{
// Use the non-scrolling interface.
// Separate the message text from any attachments in the message.
var msgAndAttachmentInfo = determineMsgAttachments(msgHeader, messageText, true);
// Only interpret @-codes if the user is reading personal email. There
// are many @-codes that do some action such as move the cursor, execute a
// script, etc., and I don't want users on message networks to do anything
// malicious to users on other BBSes.
if (this.readingPersonalEmail)
msgAndAttachmentInfo.msgText = replaceAtCodesInStr(msgAndAttachmentInfo.msgText); // Or this.ParseMsgAtCodes(msgAndAttachmentInfo.msgText, msgHeader) to replace only some @ codes
var msgTextWrapped = word_wrap(msgAndAttachmentInfo.msgText, console.screen_columns-1);
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
// Generate the key help text
var keyHelpText = "\1n\1c\1h#\1n\1b, \1c\1hLeft\1n\1b, \1c\1hRight\1n\1b, ";
if (this.CanDelete() || this.CanDeleteLastMsg())
keyHelpText += "\1c\1hDEL\1b, ";
if (this.CanEdit())
keyHelpText += "\1c\1hE\1y)\1n\1cdit\1b, ";
keyHelpText += "\1c\1hF\1y)\1n\1cirst\1b, \1c\1hL\1y)\1n\1cast\1b, \1c\1hR\1y)\1n\1ceply\1b, ";
// If the user is allowed to change to a different message area, then
// include that option.
if (allowChgMsgArea)
{
// If there's room for the private reply option, then include that
// before the change area option.
if (console.screen_columns >= 89)
keyHelpText += "\1c\1hP\1y)\1n\1crivate reply\1b, ";
keyHelpText += "\1c\1hC\1y)\1n\1chg area\1b, ";
}
else
{
// The user isn't allowed to change to a different message area.
// Go ahead and include the private reply option.
keyHelpText += "\1c\1hP\1y)\1n\1crivate reply\1b, ";
}
keyHelpText += "\1c\1hQ\1y)\1n\1cuit\1b, \1c\1h?\1g: \1c";
// User input loop
var writeMessage = true;
var writePromptText = true;
var continueOn = true;
while (continueOn)
{
if (writeMessage)
{
if (console.term_supports(USER_ANSI))
console.clear("\1n");
// Write the message header & message body to the screen
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
console.print("\1n" + this.colors["msgBodyColor"]);
console.putmsg(msgTextWrapped, P_NOATCODES);
}
// Write the prompt text
if (writePromptText)
console.print(keyHelpText);
// Default the writing of the message & input prompt to true for the
// next iteration.
writeMessage = true;
writePromptText = true;
// Input a key from the user and take action based on the keypress.
retObj.lastKeypress = getKeyWithESCChars(K_UPPER/*|K_NOCRLF|K_NOECHO|K_NOSPIN*/);
switch (retObj.lastKeypress)
{
case KEY_DEL: // Delete message
console.crlf();
// Prompt the user for confirmation to delete the message.
// Note: this.DeleteMessage() will check to see if the user
// is a sysop or the message was posted by the user.
// If the message was deleted, then exit this read method
// and return KEY_RIGHT as the last keypress so that the
// calling method will go to the next message/sub-board.
// Otherwise (if the message was not deleted), refresh the
// last 2 lines of the message on the screen.
// TODO: For the DeleteMessage() call, pass the array of file
// attachments for it to delete (i.e., msgInfo.attachments)
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
var msgWasDeleted = this.DeleteMessage(pOffset);
if (msgWasDeleted)
{
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
continueOn = msgSearchObj.continueInputLoop;
retObj.newMsgOffset = msgSearchObj.newMsgOffset;
retObj.nextAction = msgSearchObj.nextAction;
if (msgSearchObj.promptGoToNextArea)
{
if (console.yesno(this.text.goToNextMsgAreaPromptText))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = false; // No need to refresh the message
}
}
break;
case "E": // Edit the message
if (this.CanEdit())
{
console.crlf();
// Let the user edit the message if they want to
var editReturnObj = this.EditExistingMsg(pOffset);
// If the user confirmed editing the message, then see if the
// message was edited and refresh the screen accordingly.
if (editReturnObj.userConfirmed)
{
// If the message was edited, then refresh the text lines
// array and update the other message-related variables.
if (editReturnObj.msgEdited && (editReturnObj.newMsgIdx > -1))
{
// When the message is edited, the old message will be
// deleted and the edited message will be posted as a new
// message. So we should return to the caller and have it
// go directly to that new message.
continueOn = false;
retObj.newMsgOffset = editReturnObj.newMsgIdx;
}
}
}
else
{
writeMessage = false;
writePromptText = false;
}
break;
case "?": // Show help
if (!console.term_supports(USER_ANSI))
{
console.crlf();
console.crlf();
}
this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgAndAttachmentInfo.attachments.length > 0);
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
if (!console.term_supports(USER_ANSI))
{
console.crlf();
console.crlf();
}
break;
case "R": // Reply to the message
case "I": // Private reply
// If the user pressed P (private reply) while reading private
// mail, then do nothing (allow only the "R" key to reply).
// If not reading personal email, go ahead and let the user reply
// with either the "P" or "R" keypress.
var privateReply = (retObj.lastKeypress == "I");
if (privateReply && this.readingPersonalEmail)
{
writeMessage = false; // Don't re-write the current message again
writePromptText = false; // Don't write the prompt text again
}
else
{
console.crlf();
var replyRetObj = this.ReplyToMsg(msgHeader, msgAndAttachmentInfo.msgText, privateReply, pOffset);
retObj.userReplied = replyRetObj.postSucceeded;
//retObj.msgDeleted = replyRetObj.msgWasDeleted;
var msgWasDeleted = replyRetObj.msgWasDeleted;
if (msgWasDeleted)
{
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
continueOn = msgSearchObj.continueInputLoop;
retObj.newMsgOffset = msgSearchObj.newMsgOffset;
retObj.nextAction = msgSearchObj.nextAction;
if (msgSearchObj.promptGoToNextArea)
{
if (console.yesno(this.text.goToNextMsgAreaPromptText))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = true; // We want to refresh the message on the screen
}
}
else
{
// If the messagebase object was not successfully re-opened
// after posting the message, then we'll want to quit.
if (!replyRetObj.msgbaseReOpened)
{
retObj.nextAction = ACTION_QUIT;
continueOn = false;
// Display an error
console.print("\1n");
console.crlf();
console.print("\1h\1yMessagebase error after replying. Aborting.\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
}
}
break;
case "P": // Post a message
if (!this.readingPersonalEmail)
{
// Let the user post a message.
if (bbs.post_msg(this.subBoardCode))
{
// TODO: If the user is doing a search, it might be
// useful to search their new message and add it to
// the search results if it's a match.. but maybe
// not?
}
console.pause();
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
// We'll want to refresh the message & prompt text on the screen
writeMessage = true;
writePromptText = true;
}
else
{
// Don't write the current message or prompt text in the next iteration
writeMessage = false;
writePromptText = false;
}
break;
// Numeric digit: The start of a number of a message to read
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
console.crlf();
// Put the user's input back in the input buffer to
// be used for getting the rest of the message number.
console.ungetstr(retObj.lastKeypress);
// Prompt for the message number
var msgNumInput = this.PromptForMsgNum(null, this.text.readMsgNumPromptText, false, ERROR_PAUSE_WAIT_MS, false);
// Only allow reading the message if the message number is valid
// and it's not the same message number that was passed in.
if ((msgNumInput > 0) && (msgNumInput-1 != pOffset))
{
// If the message is marked as deleted, then output an error
if (this.MessageIsDeleted(msgNumInput-1))
{
console.crlf();
console.print("\1n" + this.text.msgHasBeenDeletedText.replace("%d", msgNumInput) + "\1n");
console.crlf();
console.pause();
}
else
{
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
readMsg = console.yesno("\1n" + this.colors["readMsgConfirmColor"]
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ msgNumInput + this.colors["readMsgConfirmColor"]
+ ": Are you sure");
}
if (readMsg)
{
continueOn = false;
retObj.newMsgOffset = msgNumInput - 1;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
}
}
}
break;

nightfox
committed
case enhReaderKeys.prevMsgByTitle: // Previous message by title
case enhReaderKeys.prevMsgByAuthor: // Previous message by author
case enhReaderKeys.prevMsgByToUser: // Previous message by 'to user'
case enhReaderKeys.prevMsgByThreadID: // Previous message by thread ID
// Only allow this if we aren't doing a message search.
if (!this.SearchingAndResultObjsDefinedForCurSub())
{
console.crlf(); // For the "Searching..." text

nightfox
committed
var threadPrevMsgOffset = this.FindThreadPrevOffset(msgHeader,
keypressToThreadType(retObj.lastKeypress),
false);
if (threadPrevMsgOffset > -1)
{
retObj.newMsgOffset = threadPrevMsgOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
}
}
else
{
writeMessage = false;
writePromptText = false;
}
break;

nightfox
committed
case enhReaderKeys.nextMsgByTitle: // Next message by title (subject)
case enhReaderKeys.nextMsgByAuthor: // Next message by author
case enhReaderKeys.nextMsgByToUser: // Next message by 'to user'
case enhReaderKeys.nextMsgByThreadID: // Next message by thread ID
// Only allow this if we aren't doing a message search.
if (!this.SearchingAndResultObjsDefinedForCurSub())
{
console.crlf(); // For the "Searching..." text

nightfox
committed
var threadNextMsgOffset = this.FindThreadNextOffset(msgHeader,
keypressToThreadType(retObj.lastKeypress),
false);
if (threadNextMsgOffset > -1)
{

nightfox
committed
retObj.newMsgOffset = threadNextMsgOffset;
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
}
}
else
{
writeMessage = false;
writePromptText = false;
}
break;
case KEY_LEFT: // Previous message
// TODO: Change the key for this?
// Look for a prior message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the previous message sub-board/group.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, false);
var goToPrevMessage = false;
if ((retObj.newMsgOffset > -1) || allowChgMsgArea)
{
if (retObj.newMsgOffset == -1 && !curMsgSubBoardIsLast())
{
console.crlf();
goToPrevMessage = console.yesno(this.text.goToPrevMsgAreaPromptText);
}
else
{
// We're not at the beginning of the sub-board, so it's okay to exit this
// method and go to the previous message.
goToPrevMessage = true;
}
}
if (goToPrevMessage)
{
continueOn = false;
retObj.nextAction = ACTION_GO_PREVIOUS_MSG;
}
break;
case KEY_RIGHT: // Next message
case KEY_ENTER:
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
// Look for a later message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the next message sub-board/group.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, true);
// Note: Unlike the left arrow key, we want to exit this method when
// navigating to the next message, regardless of whether or not the
// user is allowed to change to a different sub-board, so that processes
// that require continuation (such as new message scan) can continue.
// Still, if there are no more readable messages in the current sub-board
// (and thus the user would go onto the next message area), prompt the
// user whether they want to continue onto the next message area.
if (retObj.newMsgOffset == -1 && !curMsgSubBoardIsLast())
{
console.crlf();
if (console.yesno(this.text.goToNextMsgAreaPromptText))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
}
else
{
// We're not at the end of the sub-board, so it's okay to exit this
// method and go to the next message.
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
break;
case "F": // First message
// Only leave this function if we aren't already on the first message.
if (pOffset > 0)
{
continueOn = false;
retObj.nextAction = ACTION_GO_FIRST_MSG;
}
else
{
writeMessage = false;
writePromptText = false;
}
break;
case "L": // Last message
// Only leave this function if we aren't already on the last message.
if (pOffset < this.NumMessages() - 1)
{
continueOn = false;
retObj.nextAction = ACTION_GO_LAST_MSG;
}
else
{
writeMessage = false;
writePromptText = false;
}
break;

nightfox
committed
case "-": // Go to the previous message area
if (allowChgMsgArea)
{
continueOn = false;
retObj.nextAction = ACTION_GO_PREV_MSG_AREA;
}
else
{
writeMessage = false;
writePromptText = false;
}
break;

nightfox
committed
case "+": // Go to the next message area
if (allowChgMsgArea || this.doingMultiSubBoardScan)
{
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
}
else
{
writeMessage = false;
writePromptText = false;
}
break;
// H and K: Display the extended message header info/kludge lines
// (for the sysop)
case "H":
case "K":
if (gIsSysop)
{
console.crlf();
// Get an array of the extended header info/kludge lines and then
// display them.
var extdHdrInfoLines = this.GetExtdMsgHdrInfo(msgHeader, (retObj.lastKeypress == "K"));
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
if (extdHdrInfoLines.length > 0)
{
console.crlf();
for (var infoIter = 0; infoIter < extdHdrInfoLines.length; ++infoIter)
{
console.print(extdHdrInfoLines[infoIter]);
console.crlf();
}
console.pause();
}
else
{
// There are no kludge lines for this message
console.print(this.text.noKludgeLinesForThisMsgText);
console.crlf();
console.pause();
}
}
else // The user is not a sysop
{
writeMessage = false;
writePromptText = false;
}
break;
// Message list, change message area: Quit out of this input loop
// and let the calling function, this.ReadMessages(), handle the
// action.
case "M": // Message list
retObj.nextAction = ACTION_DISPLAY_MSG_LIST;
continueOn = false;
break;
case "C": // Change message area, if allowed
if (allowChgMsgArea)
{
retObj.nextAction = ACTION_CHG_MSG_AREA;
continueOn = false;
}
else
{
writeMessage = false;
writePromptText = false;
}
break;
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
case enhReaderKeys.downloadAttachments: // Download attachments
if (msgAndAttachmentInfo.attachments.length > 0)
{
console.print("\1n");
console.crlf();
console.print("\1c- Download Attached Files -\1n");
// Note: sendAttachedFiles() will output a CRLF at the beginning.
sendAttachedFiles(msgAndAttachmentInfo.attachments);
// Ensure the message is refreshed on the screen
writeMessage = true;
writePromptText = true;
}
else
{
writeMessage = false;
writePromptText = false;
}
break;
case enhReaderKeys.saveToBBSMachine:
// Save the message to the BBS machine - Only allow this
// if the user is a sysop.
if (gIsSysop)
{
console.crlf();
console.print("\1n\1cFilename:\1h");
var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1
var filename = console.getstr(inputLen, K_NOCRLF);
console.print("\1n");
console.crlf();
if (filename.length > 0)
{
var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true);
if (saveMsgRetObj.succeeded)
console.print("\1n\1cThe message has been saved.\1n");
else
console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
else
{
console.print("\1n\1y\1hMessage not exported\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
writeMessage = true;
}
else
writeMessage = false;
break;
case "Q": // Quit
retObj.nextAction = ACTION_QUIT;
continueOn = false;
break;
default:
// No need to do anything
writeMessage = false;
writePromptText = false;
break;
}
}
}
// Mark the message as read if it was written to the current user or if
// the user is reading personal email.
if (userNameHandleAliasMatch(msgHeader.to))
{
msgHeader.attr |= MSG_READ;
this.msgbase.put_msg_header(true, msgHeader.offset, msgHeader);
if (this.SearchTypePopulatesSearchResults())
this.RefreshSearchResultMsgHdr(pOffset, MSG_READ);
}
// If not reading personal email, then update the scan & last read message pointers.
if (this.subBoardCode != "mail") // && !this.SearchTypePopulatesSearchResults()
{
if (msgHeader.number > msg_area.sub[this.subBoardCode].scan_ptr)
msg_area.sub[this.subBoardCode].scan_ptr = msgHeader.number;
msg_area.sub[this.subBoardCode].last_read = msgHeader.number;
}
return retObj;
} // DigDistMsgReader_ReadMessageEnhanced
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
// For the DigDistMsgReader class: For the enhanced reader method - Prepares the
// last 2 lines on the screen for propmting the user for something.
//
// Return value: An object containing x and y values representing the cursor
// position, ready to prompt the user.
function DigDistMsgReader_EnhReaderPrepLast2LinesForPrompt()
{
var promptPos = { x: this.msgAreaLeft, y: this.msgAreaBottom };
// Write a line of characters above where the prompt will be placed,
// to help get the user's attention.
console.gotoxy(promptPos.x, promptPos.y-1);
console.print("\1n" + this.colors.enhReaderPromptSepLineColor);
for (var lineCounter = 0; lineCounter < this.msgAreaWidth; ++lineCounter)
console.print(HORIZONTAL_SINGLE);
// Clear the inside of the message area, so as not to overwrite
// the scrollbar character
console.print("\1n");
console.gotoxy(promptPos);
for (var lineCounter = 0; lineCounter < this.msgAreaWidth; ++lineCounter)
console.print(" ");
// Position the cursor at the prompt location
console.gotoxy(promptPos);
return promptPos;
}
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
// For the DigDistMsgReader class: For the enhanced reader method - Looks for a
// later method that isn't marked for deletion. If none is found, looks for a
// prior message that isn't marked for deletion.
//
// Parameters:
// pOffset: The offset of the message to start at
//
// Return value: An object with the following properties:
// newMsgOffset: The offset of the next readable message
// nextAction: The next action (code) for the enhanced reader
// continueInputLoop: Boolean - Whether or not to continue the input loop
// promptGoToNextArea: Boolean - Whether or not to prompt the user to go
// to the next message area
function DigDistMsgReader_LookForNextOrPriorNonDeletedMsg(pOffset)
{
var retObj = new Object();
retObj.newMsgOffset = 0;
retObj.nextAction = ACTION_NONE;
retObj.continueInputLoop = true;
retObj.promptGoToNextArea = false;
// Look for a later message that isn't marked for deletion.
// If none is found, then look for a prior message that isn't
// marked for deletion.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, true);
if (retObj.newMsgOffset > -1)
{
retObj.continueInputLoop = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
{
// No later message found, so look for a prior message.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, false);
if (retObj.newMsgOffset > -1)
{
retObj.continueInputLoop = false;
retObj.nextAction = ACTION_GO_PREVIOUS_MSG;
}
else
{
// No prior message found. We'll want to return from the enhanced
// reader function (with message index -1) so that this script can
// go onto the next message sub-board/group. Also, set the next
// action such that the calling method will go on to the next
// message/sub-board.
if (!curMsgSubBoardIsLast())
{
if (this.readingPersonalEmail)
{
retObj.continueInputLoop = false;
retObj.nextAction = ACTION_QUIT;
}
else
retObj.promptGoToNextArea = true;
}
else
{
// We're not at the end of the sub-board or the current sub-board
// is the last, so go ahead and exit.
retObj.continueInputLoop = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
}
}
return retObj;
}
// For the DigDistMsgReader class: Writes the help line for enhanced reader
// mode.
//
// Parameters:
// pScreenRow: Optional - The screen row to write the help line on. If not
// specified, the last row on the screen will be used.
// pDisplayChgAreaOpt: Optional boolean - Whether or not to show the "change area" option.
// Defaults to true.
function DigDistMsgReader_DisplayEnhancedMsgReadHelpLine(pScreenRow, pDisplayChgAreaOpt)
{
var displayChgAreaOpt = (typeof(pDisplayChgAreaOpt) == "boolean" ? pDisplayChgAreaOpt : true);
// Move the cursor to the desired location on the screen and display the help line
console.gotoxy(1, typeof(pScreenRow) == "number" ? pScreenRow : console.screen_rows);
console.print(displayChgAreaOpt ? this.enhReadHelpLine : this.enhReadHelpLineWithoutChgArea);
}
// For the DigDistMsgReader class: Goes back to the prior readable sub-board
// (accounting for search results, etc.). Changes the object's subBoardCode,
// msgbase object, etc.
//
// Parameters:
// pAllowChgMsgArea: Boolean - Whether or not the user is allowed to change
// to another message area
// Return value: An object with the following properties:
// changedMsgArea: Boolean - Whether or not this method successfully
// changed to a prior message area
// msgIndex: The message index for the new sub-board. Will be -1
// if there is no new sub-board or otherwise invalid
// scenario.
// shouldStopReading: Whether or not the script should stop letting
// the user read messages
function DigDistMsgReader_GoToPrevSubBoardForEnhReader(pAllowChgMsgArea)
{
var retObj = new Object();
retObj.changedMsgArea = false;
retObj.msgIndex = -1;
retObj.shouldStopReading = false;
// Only allow this if pAllowChgMsgArea is true and we're not reading personal
// email. If we're reading personal email, then msg_area.sub is unavailable
// for the "mail" internal code.
if (pAllowChgMsgArea && (this.subBoardCode != "mail"))
{
// continueGoingToPrevSubBoard specifies whether or not to continue
// going to the previous sub-boards in case there is search text
// specified.
var continueGoingToPrevSubBoard = true;
while (continueGoingToPrevSubBoard)
{
// Allow going to the previous message sub-board/group.
var msgGrpIdx = msg_area.sub[this.subBoardCode].grp_index;
var subBoardIdx = msg_area.sub[this.subBoardCode].index;
var readMsgRetObj = findNextOrPrevNonEmptySubBoard(msgGrpIdx, subBoardIdx, false);
// If a different sub-board was found, then go to that sub-board.
if (readMsgRetObj.foundSubBoard && readMsgRetObj.subChanged)
{
bbs.cursub = 0;
bbs.curgrp = readMsgRetObj.grpIdx;
bbs.cursub = readMsgRetObj.subIdx;
// Open the new sub-board
this.msgbase.close();
this.setSubBoardCode(readMsgRetObj.subCode);
this.msgbase = new MsgBase(this.subBoardCode);
if (this.msgbase.open())
{
if (this.searchType == SEARCH_NONE || !this.SearchingAndResultObjsDefinedForCurSub())
{
continueGoingToPrevSubBoard = false; // No search results, so don't keep going to the previous sub-board.
// Go to the user's last read message. If the message index ends up
// below 0, then go to the last message not marked as deleted.

nightfox
committed
retObj.msgIndex = this.AbsMsgNumToIdx(msg_area.sub[this.subBoardCode].last_read);
if (retObj.msgIndex >= 0)
retObj.changedMsgArea = true;
else
{
// Look for the last message not marked as deleted
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(this.NumMessages(), false);

nightfox
committed
// If a non-deleted message was found, then set retObj.msgIndex to it.
// Otherwise, tell the user there are no messages in this sub-board
// and return.
if (nonDeletedMsgIdx > -1)

nightfox
committed
retObj.msgIndex = nonDeletedMsgIdx;
else

nightfox
committed
retObj.msgIndex = this.NumMessages() - 1; // Shouldn't get here
var newLastRead = this.IdxToAbsMsgNum(retObj.msgIndex);
if (newLastRead > -1)
msg_area.sub[this.subBoardCode].last_read = newLastRead;
}
}
// Set the hotkey help line again, as this sub-board might have
// different settings for whether messages can be edited or deleted,
// then refresh it on the screen.
var oldHotkeyHelpLine = this.enhReadHelpLine;
this.SetEnhancedReaderHelpLine();
// If a search is is specified that would populate the search
// results, then populate this.msgSearchHdrs for the current
// sub-board if there is search text specified. If there
// are no search results, then ask the user if they want
// to continue searching the message areas.
if (this.SearchTypePopulatesSearchResults())
{
if (this.PopulateHdrsIfSearch_DispErrorIfNoMsgs(false, true, false))
{
retObj.changedMsgArea = true;
continueGoingToPrevSubBoard = false;

nightfox
committed
retObj.msgIndex = this.NumMessages() - 1;
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
}
else // No search results in this sub-board
{
continueGoingToPrevSubBoard = !console.noyes("Continue searching");
if (!continueGoingToPrevSubBoard)
{
retObj.shouldStopReading = true;
return retObj;
}
}
}
else
{
retObj.changedMsgArea = true;
if ((oldHotkeyHelpLine != this.enhReadHelpLine) && this.scrollingReaderInterface && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
}
}
else // The message base failed to open
{
console.clear("\1n");
console.print("\1h\1y* \1wUnable to open message sub-board:");
console.crlf();
console.print(subBoardGrpAndName(this.subBoardCode));
console.crlf();
console.pause();
retObj.shouldStopReading = true;
continueGoingToPrevSubBoard = false;
return retObj;
}
}
else
{
// Didn't find a prior sub-board with readable messages.
// We could stop and exit the script here by doing the following,
// but I'd rather let the user exit when they want to.
continueGoingToPrevSubBoard = false;
// Show a message telling the user that there are no prior
// messages or sub-boards. Then, refresh the hotkey help line.
writeWithPause(this.msgAreaLeft, console.screen_rows,
"\1n\1h\1y* No prior messages or no message in prior message areas.",
ERROR_PAUSE_WAIT_MS, "\1n", true);
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, pAllowChgMsgArea);
}
}
}
return retObj;
}
// For the DigDistMsgReader class: Goes to the next readable sub-board
// (accounting for search results, etc.). Changes the object's subBoardCode,
// msgbase object, etc.
//
// Parameters:
// pAllowChgMsgArea: Boolean - Whether or not the user is allowed to change
// to another message area
// Return value: An object with the following properties:
// changedMsgArea: Boolean - Whether or not this method successfully
// changed to a prior message area
// msgIndex: The message index for the new sub-board. Will be -1
// if there is no new sub-board or otherwise invalid
// scenario.
// shouldStopReading: Whether or not the script should stop letting
// the user read messages
function DigDistMsgReader_GoToNextSubBoardForEnhReader(pAllowChgMsgArea)
{
var retObj = new Object();
retObj.changedMsgArea = false;
retObj.msgIndex = -1;
retObj.shouldStopReading = false;
// Only allow this if pAllowChgMsgArea is true and we're not reading personal
// email. If we're reading personal email, then msg_area.sub is unavailable
// for the "mail" internal code.
if (pAllowChgMsgArea && (this.subBoardCode != "mail"))
{
// continueGoingToNextSubBoard specifies whether or not to continue
// advancing to the next sub-boards in case there is search text
// specified.
var continueGoingToNextSubBoard = true;
while (continueGoingToNextSubBoard)
{
// Allow going to the next message sub-board/group.
var msgGrpIdx = msg_area.sub[this.subBoardCode].grp_index;
var subBoardIdx = msg_area.sub[this.subBoardCode].index;
var readMsgRetObj = findNextOrPrevNonEmptySubBoard(msgGrpIdx, subBoardIdx, true);
// If a different sub-board was found, then go to that sub-board.
if (readMsgRetObj.foundSubBoard && readMsgRetObj.subChanged)
{
retObj.msgIndex = 0;
bbs.cursub = 0;
bbs.curgrp = readMsgRetObj.grpIdx;
bbs.cursub = readMsgRetObj.subIdx;
// Open the new sub-board
this.msgbase.close();
this.setSubBoardCode(readMsgRetObj.subCode);
this.msgbase = new MsgBase(this.subBoardCode);
if (this.msgbase.open())
{
if ((this.searchType == SEARCH_NONE) || !this.SearchingAndResultObjsDefinedForCurSub())
{
continueGoingToNextSubBoard = false; // No search results, so don't keep going to the next sub-board.
// Go to the user's last read message. If the message index ends up
// below 0, then go to the first message not marked as deleted.
retObj.msgIndex = this.AbsMsgNumToIdx(msg_area.sub[this.subBoardCode].last_read);

nightfox
committed
if (retObj.msgIndex >= 0)
retObj.changedMsgArea = true;
else
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
{
// Set the index of the message to display - Look for the
// first message not marked as deleted
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(this.NumMessages()-1, true);
// If a non-deleted message was found, then set retObj.msgIndex to it.
// Otherwise, tell the user there are no messages in this sub-board
// and return.
if (nonDeletedMsgIdx > -1)
{
retObj.msgIndex = nonDeletedMsgIdx;
retObj.changedMsgArea = true;
var newLastRead = this.IdxToAbsMsgNum(nonDeletedMsgIdx);
if (newLastRead > -1)
msg_area.sub[this.subBoardCode].last_read = newLastRead;
}
}
}
// Set the
Loading
Loading full blame...