Newer
Older

nightfox
committed
// $Id$
/* 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)
* ... Comments trimmed ...
* 2019-07-06 Eric Oulashin Version 1.23 Beta 1
* Updated DigDistMsgReader_GetMsgBody() to remove any initial
* color code(s) from the start of the message, which can
* color the whole message unnecessarily.
* 2019-07-26 Eric Oulashin Started working on supporting utf-8 text conversion to cp437.
* 2019-07-27 Eric Oulashin Version 1.23
* Releasing this version
* 2019-08-15 Eric Oulashin Version 1.24 Beta
* When making a private reply on local email,
* an error is now outputted if the recipient's
* user number is not found.
* Also, fixed an 'undefined' bug that happened when searching
* for messages sometimes. searchMsgbase() referenced
* this.subBoardCode, but that function isn't
* part of an object, so this.subBoardCode isn't available
* there.
* 2019-08-17 Eric Oulashin Verison 1.24
* Releasing this version
* 2019-08-23 Eric Oulashin Version 1.25 Beta
* Started working on adding searching when changing
* to another message area.
* 2019-08-29 Eric Oulashin Version 1.25
* Releasing this version
* 2019-09-12 Eric Oulashin Version 1.26
* Fixed a bug that caused some tally information to be
* displayed as "undefined".
*/
/* 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.
-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.
*/
// TODOs - For sub-board area search:
// - Enable searching in traditional interface
// - Update the keys in the lightbar help line and traditional interface
const requireFnExists = (typeof(require) === "function");
if (requireFnExists)
{
require("sbbsdefs.js", "K_UPPER");
require("text.js", "Email"); // Text string definitions (referencing text.dat)
require("utf8_cp437.js", "utf8_cp437");
require("userdefs.js", "USER_UTF8");
}
else
{
load("sbbsdefs.js");
load("text.js"); // Text string definitions (referencing text.dat)
load("utf8_cp437.js");
load("userdefs.js");
// 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.26";
var READER_DATE = "2019-09-12";
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Keyboard key codes for displaying on the screen
var UP_ARROW = ascii(24);
var DOWN_ARROW = ascii(25);
var LEFT_ARROW = ascii(17);
var RIGHT_ARROW = ascii(16);
// 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;
// PageUp & PageDown keys - Synchronet 3.17 as of about December 18, 2017
// use CTRL-P and CTRL-N for PageUp and PageDown, respectively. sbbsdefs.js
// defines them as KEY_PAGEUP and KEY_PAGEDN; I've used slightly different names
// in this script so that this script will work with Synchronet systems before
// and after the update containing those key definitions.
var KEY_PAGE_UP = CTRL_P;
var KEY_PAGE_DOWN = CTRL_N;
// Ensure KEY_PAGE_UP and KEY_PAGE_DOWN are set to what's defined in sbbs.js
// for KEY_PAGEUP and KEY_PAGEDN in case they change
if (typeof(KEY_PAGEUP) === "string")
KEY_PAGE_UP = KEY_PAGEUP;
if (typeof(KEY_PAGEDN) === "string")
KEY_PAGE_DOWN = KEY_PAGEDN;
// 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)
// These were added to sbbsdef.js around December 17, 2017:
//var KEY_PAGEUP ='\x10'; /* ctrl-p (Page Up) */
//var KEY_PAGEDN ='\x0e'; /* ctrl-n (Page Down) */
// Characters for display
// Box-drawing/border characters: Single-line
var UPPER_LEFT_SINGLE = "\xDA";
var HORIZONTAL_SINGLE = "\xC4";
var UPPER_RIGHT_SINGLE = "\xBF";
var VERTICAL_SINGLE = "\xB3";
var LOWER_LEFT_SINGLE = "\xC0";
var LOWER_RIGHT_SINGLE = "\xD9";
var T_SINGLE = "\xC2";
var LEFT_T_SINGLE = "\xC3";
var RIGHT_T_SINGLE = "\xB4";
var BOTTOM_T_SINGLE = "\xC1";
var CROSS_SINGLE = "\xC5";
// Box-drawing/border characters: Double-line
var UPPER_LEFT_DOUBLE = "\xC9";
var HORIZONTAL_DOUBLE = "\xCD";
var UPPER_RIGHT_DOUBLE = "\xBB";
var VERTICAL_DOUBLE = "\xBA";
var LOWER_LEFT_DOUBLE = "\xC8";
var LOWER_RIGHT_DOUBLE = "\xBC";
var T_DOUBLE = "\xCB";
var LEFT_T_DOUBLE = "\xCC";
var RIGHT_T_DOUBLE = "\xB9";
var BOTTOM_T_DOUBLE = "\xCA";
var CROSS_DOUBLE = "\xCE";
// Box-drawing/border characters: Vertical single-line with horizontal double-line
var UPPER_LEFT_VSINGLE_HDOUBLE = "\xD5";
var UPPER_RIGHT_VSINGLE_HDOUBLE = "\xB8";
var LOWER_LEFT_VSINGLE_HDOUBLE = "\xD4";
var LOWER_RIGHT_VSINGLE_HDOUBLE = "\xBE";
// Other special characters
var DOT_CHAR = "\xF9";
var CHECK_CHAR = "\xFB";
var THIN_RECTANGLE_LEFT = "\xDD";
var THIN_RECTANGLE_RIGHT = "\xDE";
var BLOCK1 = "\xB0"; // Dimmest block
var BLOCK2 = "\xB1";
var BLOCK3 = "\xB2";
var BLOCK4 = "\xDB"; // Brightest block
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
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;
// Definitions for help line refresh parameters for error functions
const REFRESH_MSG_AREA_CHG_LIGHTBAR_HELP_LINE = 0;
// Misc. defines
var ERROR_WAIT_MS = 1500;
var SEARCH_TIMEOUT_MS = 10000;
// 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";
if (typeof(MSG_TRUNCFILE) != "undefined")
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";
if (typeof(MSG_GATE) != "undefined")
gNetMsgAttrStrs[MSG_GATE] = "Gate";
if (typeof(MSG_ORPHAN) != "undefined")
gNetMsgAttrStrs[MSG_ORPHAN] = "Orphan";
if (typeof(MSG_FPU) != "undefined")
gNetMsgAttrStrs[MSG_FPU] = "FPU";
if (typeof(MSG_TYPELOCAL) != "undefined")
gNetMsgAttrStrs[MSG_TYPELOCAL] = "ForLocal";
if (typeof(MSG_TYPEECHO) != "undefined")
gNetMsgAttrStrs[MSG_TYPEECHO] = "ForEcho";
if (typeof(MSG_TYPENET) != "undefined")
gNetMsgAttrStrs[MSG_TYPENET] = "ForNetmail";
if (typeof(MSG_MIMEATTACH) != "undefined")
gNetMsgAttrStrs[MSG_MIMEATTACH] = "MimeAttach";

nightfox
committed
// A regular expression to check whether a string is an email address

nightfox
committed
var gEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
// A regular expression to check whether a string is a FidoNet email address
var gFTNEmailregex = /^.*@[0-9]+:[0-9]+\/[0-9]+$/;

nightfox
committed
// An array of regular expressions for checking for ANSI codes (globally in a string & ignore case)
var gANSIRegexes = [ new RegExp(ascii(27) + "\[[0-9]+[mM]", "gi"),
new RegExp(ascii(27) + "\[[0-9]+(;[0-9]+)+[mM]", "gi"),
new RegExp(ascii(27) + "\[[0-9]+[aAbBcCdD]", "gi"),
new RegExp(ascii(27) + "\[[0-9]+;[0-9]+[hHfF]", "gi"),
new RegExp(ascii(27) + "\[[sSuUkK]", "gi"),
new RegExp(ascii(27) + "\[2[jJ]", "gi") ];
// 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)
{
if (requireFnExists)
require("frame.js", "Frame");
else
load("frame.js");
}
var gScrollbarJSAvailable = file_exists(backslash(system.exec_dir) + "load/scrollbar.js");
if (gScrollbarJSAvailable)
{
if (requireFnExists)
require("scrollbar.js", "ScrollBar");
else
load("scrollbar.js");
}

nightfox
committed
// See if the avatar support files are available, and load them if so
var gAvatar = null;
if (file_exists(backslash(system.exec_dir) + "load/smbdefs.js") && file_exists(backslash(system.exec_dir) + "load/avatar_lib.js"))
{
if (requireFnExists)
require("smbdefs.js", "SMB_POLL_ANSWER");
else
load("smbdefs.js");

nightfox
committed
gAvatar = load({}, "avatar_lib.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
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
491
492
493
494
495
496
497
498
499
500
501
502
503
504
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)
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
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 & ANSI temp directories if they exists
deltree(gFileAttachDir);
deltree(backslash(system.node_dir + "DDMsgReaderANSIMsgTemp"));
// 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");
}
// 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)
{
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
// Set the methods for the object
this.setSubBoardCode = DigDistMsgReader_SetSubBoardCode;
this.RecalcMsgListWidthsAndFormatStrs = DigDistMsgReader_RecalcMsgListWidthsAndFormatStrs;
this.NumMessages = DigDistMsgReader_NumMessages;
this.SearchingAndResultObjsDefinedForCurSub = DigDistMsgReader_SearchingAndResultObjsDefinedForCurSub;
this.PopulateHdrsForCurrentSubBoard = DigDistMsgReader_PopulateHdrsForCurrentSubBoard;
this.FilterMsgHdrsIntoHdrsForCurrentSubBoard = DigDistMsgReader_FilterMsgHdrsIntoHdrsForCurrentSubBoard;
this.GetMsgIdx = DigDistMsgReader_GetMsgIdx;
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.RefreshHdrInSubBoardHdrs = DigDistMsgReader_RefreshHdrInSubBoardHdrs;
this.RefreshHdrInSavedArrays = DigDistMsgReader_RefreshHdrInSavedArrays;
this.ReadMessages = DigDistMsgReader_ReadMessages;
this.DisplayEnhancedMsgReadHelpLine = DigDistMsgReader_DisplayEnhancedMsgReadHelpLine;
this.GoToPrevSubBoardForEnhReader = DigDistMsgReader_GoToPrevSubBoardForEnhReader;
this.GoToNextSubBoardForEnhReader = DigDistMsgReader_GoToNextSubBoardForEnhReader;
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;
this.WriteMsgListScreenTopHeader = DigDistMsgReader_WriteMsgListScreenTopHeader;
this.ReadMessageEnhanced = DigDistMsgReader_ReadMessageEnhanced;
this.ReadMessageEnhanced_Scrollable = DigDistMsgReader_ReadMessageEnhanced_Scrollable;
this.ReadMessageEnhanced_Traditional = DigDistMsgReader_ReadMessageEnhanced_Traditional;
this.EnhReaderPrepLast2LinesForPrompt = DigDistMsgReader_EnhReaderPrepLast2LinesForPrompt;
this.LookForNextOrPriorNonDeletedMsg = DigDistMsgReader_LookForNextOrPriorNonDeletedMsg;
this.PrintMessageInfo = DigDistMsgReader_PrintMessageInfo;
this.ListScreenfulOfMessages = DigDistMsgReader_ListScreenfulOfMessages;
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.NonDeletedMessagesExist = DigDistMsgReader_NonDeletedMessagesExist;
this.HighestMessageNum = DigDistMsgReader_HighestMessageNum;
this.IsValidMessageNum = DigDistMsgReader_IsValidMessageNum;
this.PromptForMsgNum = DigDistMsgReader_PromptForMsgNum;
this.ParseMsgAtCodes = DigDistMsgReader_ParseMsgAtCodes;
this.ReplaceMsgAtCodeFormatStr = DigDistMsgReader_ReplaceMsgAtCodeFormatStr;
this.FindNextNonDeletedMsgIdx = DigDistMsgReader_FindNextNonDeletedMsgIdx;
this.ChangeSubBoard = DigDistMsgReader_ChangeSubBoard;
this.EnhancedReaderChangeSubBoard = DigDistMsgReader_EnhancedReaderChangeSubBoard;
this.ReplyToMsg = DigDistMsgReader_ReplyToMsg;
this.DoPrivateReply = DigDistMsgReader_DoPrivateReply;
this.DisplayEnhancedReaderHelp = DigDistMsgReader_DisplayEnhancedReaderHelp;
this.DisplayEnhancedMsgHdr = DigDistMsgReader_DisplayEnhancedMsgHdr;
this.DisplayAreaChgHdr = DigDistMsgReader_DisplayAreaChgHdr;
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.PromptAndDeleteMessage = DigDistMsgReader_PromptAndDeleteMessage;
this.PromptAndDeleteSelectedMessages = DigDistMsgReader_PromptAndDeleteSelectedMessages;
this.GetExtdMsgHdrInfo = DigDistMsgReader_GetExtdMsgHdrInfo;
this.GetMsgInfoForEnhancedReader = DigDistMsgReader_GetMsgInfoForEnhancedReader;
this.GetLastReadMsgIdx = DigDistMsgReader_GetLastReadMsgIdx;
this.GetScanPtrMsgIdx = DigDistMsgReader_GetScanPtrMsgIdx;
this.RemoveFromSearchResults = DigDistMsgReader_RemoveFromSearchResults;
this.FindThreadNextOffset = DigDistMsgReader_FindThreadNextOffset;
this.FindThreadPrevOffset = DigDistMsgReader_FindThreadPrevOffset;
this.SaveMsgToFile = DigDistMsgReader_SaveMsgToFile;
this.ToggleSelectedMessage = DigDistMsgReader_ToggleSelectedMessage;
this.MessageIsSelected = DigDistMsgReader_MessageIsSelected;
this.AllSelectedMessagesCanBeDeleted = DigDistMsgReader_AllSelectedMessagesCanBeDeleted;
this.DeleteSelectedMessages = DigDistMsgReader_DeleteSelectedMessages;
this.NumSelectedMessages = DigDistMsgReader_NumSelectedMessages;
this.ForwardMessage = DigDistMsgReader_ForwardMessage;
this.VoteOnMessage = DigDistMsgReader_VoteOnMessage;
this.HasUserVotedOnMsg = DigDistMsgReader_HasUserVotedOnMsg;
this.GetUpvoteAndDownvoteInfo = DigDistMsgReader_GetUpvoteAndDownvoteInfo;
this.GetMsgBody = DigDistMsgReader_GetMsgBody;
this.RefreshMsgHdrInArrays = DigDistMsgReader_RefreshMsgHdrInArrays;
this.WriteLightbarKeyHelpErrorMsg = DigDistMsgReader_WriteLightbarKeyHelpErrorMsg;
// 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 the command-line
// arguments can specify another mode.
this.startMode = READER_MODE_LIST;
// hdrsForCurrentSubBoard is an array that will be populated with the
// message headers for the current sub-board.
this.hdrsForCurrentSubBoard = new Array();
// hdrsForCurrentSubBoardByMsgNum is an object that maps absolute message numbers
// to their index to hdrsForCurrentSubBoard
this.hdrsForCurrentSubBoardByMsgNum = new Object();
// 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.doingMsgScan = false; // Set to true in MessageAreaScan()
this.subBoardCode = bbs.cursub_code; // The message sub-board code
this.readingPersonalEmail = false;
// this.colors will be an array of colors to use in the message list
this.colors = getDefaultColors();
this.readingPersonalEmailFromUser = false;

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

nightfox
committed
var subCodeLowerCase = pSubBoardCode.toLowerCase();
if (subBoardCodeIsValid(subCodeLowerCase))
{
this.setSubBoardCode(subCodeLowerCase);
if (gCmdLineArgVals.hasOwnProperty("personalemailsent") && gCmdLineArgVals.personalemailsent)
this.readingPersonalEmailFromUser = true;
}
}
// This property controls whether or not the user will be prompted to
// continue listing messages after selecting a message to read. Only for
// regular reading, not for newscans etc.
this.promptToContinueListingMessages = false;
// Whether or not to prompt the user to confirm to read a message
this.promptToReadMessage = false;
// For enhanced reader mode (reading only, not for newscan, etc.): Whether or
// not to ask the user whether to post on the sub-board in reader mode after
// reading the last message instead of prompting to go to the next sub-board.
// This is like the stock Synchronet behavior.
this.readingPostOnSubBoardInsteadOfGoToNext = false;
// String lengths for the columns to write
// Fixed field widths: Message number, date, and time
// TODO: It might be good to figure out the longest message number for a
// sub-board and set the message number length dynamically. It would have
// to change whenever the user changes to a different sub-board, and the
// message list format string would have to change too.
//this.MSGNUM_LEN = 4;
this.MSGNUM_LEN = 5;
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
this.FROM_LEN = (console.screen_columns * (15/console.screen_columns)).toFixed(0);
this.TO_LEN = (console.screen_columns * (15/console.screen_columns)).toFixed(0);
var colsLeftForSubject = console.screen_columns-this.MSGNUM_LEN-this.DATE_LEN-this.TIME_LEN-this.FROM_LEN-this.TO_LEN-6; // 6 to account for the spaces
this.SUBJ_LEN = (console.screen_columns * (colsLeftForSubject/console.screen_columns)).toFixed(0);
// For showing message scores in the message list
this.SCORE_LEN = 4;
// Whether or not to show message scores in the message list: Only if the terminal
// is at least 86 characters wide and if vote functions exist in the running build
// of Synchronet
this.showScoresInMsgList = ((console.screen_columns >= 86) && (typeof((new MsgBase("mail")).vote_msg) === "function"));
// 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;
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
// 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;
// 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)";
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.delSelectedMsgsConfirmText = "\1n\1h\1yDelete selected messages: Are you sure";
this.text.msgDeletedText = "\1n\1cMessage #\1h%d\1n\1c has been marked for deletion.";
this.text.selectedMsgsDeletedText = "\1n\1cSelected messages have 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.cannotDeleteAllSelectedMsgsText = "\1n\1y\1h* Cannot delete all selected messages";
this.text.msgEditConfirmText = "\1n\1cEdit message #\1h%d\1n\1c: Are you sure";
this.text.noPersonalEmailText = "\1n\1cYou have no messages.";
// 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;
// For the message area chooser header filename & maximum number of
// area chooser header lines to display
this.areaChooserHdrFilenameBase = "areaChgHeader";
this.areaChooserHdrMaxLines = 5;
// Some key bindings for enhanced reader mode
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
this.enhReaderKeys = {
reply: "R",
privateReply: "I",
editMsg: "E",
showHelp: "?",
postMsg: "P",
nextMsg: KEY_RIGHT,
previousMsg: KEY_LEFT,
firstMsg: "F",
lastMsg: "L",
showKludgeLines: "K",
showHdrInfo: "H",
showMsgList: "M",
chgMsgArea: "C",
userEdit: "U",
quit: "Q",
prevMsgByTitle: "<",
nextMsgByTitle: ">",
prevMsgByAuthor: "{",
nextMsgByAuthor: "}",
prevMsgByToUser: "[",
nextMsgByToUser: "]",
prevMsgByThreadID: "(",
nextMsgByThreadID: ")",
prevSubBoard: "-",
nextSubBoard: "+",
downloadAttachments: CTRL_A,
saveToBBSMachine: CTRL_S,
deleteMessage: KEY_DEL,
selectMessage: " ",
batchDelete: CTRL_D,
forwardMsg: "O",
vote: "V",
showVotes: "T",
closePoll: "!",
bypassSubBoardInNewScan: "B"
};
if (gIsSysop)
this.enhReaderKeys.validateMsg = "A";

nightfox
committed
// Whether or not to display avatars
this.displayAvatars = true;
this.rightJustifyAvatar = true;

nightfox
committed
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"];
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
1007
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
// 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", "");
// Calculate the message list widths and format strings based on the current
// sub-board code and color settings. Start with a message # field length
// of 4 characters. This will be re-calculated later after message headers
// are loaded.
this.RecalcMsgListWidthsAndFormatStrs(4);
// 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 = loadTextFileIntoArray("enhMsgHeader", 10);
// If the header file didn't exist, then populate the enhanced reader header
// array with default lines.
this.usingInternalEnhMsgHdr = (this.enhMsgHeaderLines.length == 0);
if (this.usingInternalEnhMsgHdr)
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
{
// 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_AND_FROM_NET-L";
numChars = console.screen_columns - 36;
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;
numChars = console.screen_columns - 67;
for (var i = 0; i < numChars; ++i)
hdrLine6 += "#";
//hdrLine6 += "@\1n\1c" + VERTICAL_SINGLE;
hdrLine6 += "@ @MSG_TIMEZONE@\1n";
for (var i = 0; i < 40; ++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 += "\1n\1c" + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\1h"
+ HORIZONTAL_SINGLE + BOTTOM_T_SINGLE;
this.enhMsgHeaderLines.push(hdrLine7);
}
// 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;
}
}
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
// 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.ValidateMsg = DigDistMsgReader_ValidateMsg;
this.GetGroupNameAndDesc = DigDistMsgReader_GetGroupNameAndDesc;
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
// 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
this.lightbarAreaChooserHelpLine = "\1n"
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + ""
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
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + ""
+ 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 + "PgUp"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "/"
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "Dn"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ 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 + "CTRL-F"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "/"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "N"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "Q"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "uit, "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "?";
// Pad the lightbar key help text on either side to center it on the screen
// (but leave off the last character to avoid screen drawing issues)
var textLen = strip_ctrl(this.lightbarAreaChooserHelpLine).length;
var padLen = console.screen_columns - textLen - 1;
var leftPadLen = Math.floor(padLen/2);
var rightPadLen = padLen - leftPadLen - 2;
this.lightbarAreaChooserHelpLine = this.colors.lightbarAreaChooserHelpLineGeneralColor
+ format("%" + leftPadLen + "s", "")
+ this.lightbarAreaChooserHelpLine
+ this.colors.lightbarAreaChooserHelpLineGeneralColor
+ format("%" + rightPadLen + "s", "") + "\1n";
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
// 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;
// selectedMessages will be an object (indexed by sub-board internal code)
// containing objects that contain message indexes (as properties) for the
// sub-boards. Messages can be selected by the user for doing things such
// as a batch delete, etc.
this.selectedMessages = new Object();
// areaChangeHdrLines is an array of text lines to use as a header to display
// above the message area changer lists.
this.areaChangeHdrLines = loadTextFileIntoArray(this.areaChooserHdrFilenameBase, this.areaChooserHdrMaxLines);

nightfox
committed
// pausePromptText is the text that will be used for some of the pause
// prompts. It's loaded from text.dat, but in case that text contains
// "@EXEC:" (to execute a script), this script will default to a "press
// a key" message.
this.pausePromptText = bbs.text(Pause);
if (this.pausePromptText.toUpperCase().indexOf("@EXEC:") > -1)
this.pausePromptText = "\1n\1c[ Press a key ] ";
}
// 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.toLowerCase() == "mail");
}
// For the DigDistMsgReader class: Populates the hdrsForCurrentSubBoard
// array with message headers from the current sub-board. Filters out
// messages that are deleted, unvalidated, private, and voting messages.
function DigDistMsgReader_PopulateHdrsForCurrentSubBoard()
{
if (this.subBoardCode == "mail")
{
this.hdrsForCurrentSubBoard = [];
this.hdrsForCurrentSubBoardByMsgNum = new Object();
return;
}
var tmpHdrs = null;
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
// First get all headers in a temporary array, then filter them into
// this.hdrsForCurrentSubBoard.
// If get_all_msg_headers exists as a function, then use it. Otherwise,
// iterate through all message offsets and get the headers.
if (typeof(msgbase.get_all_msg_headers) === "function")
{
// Pass false to get_all_msg_headers() to tell it not to return vote messages
// (the parameter was introduced in Synchronet 3.17+)
tmpHdrs = msgbase.get_all_msg_headers(false);
}
else
{
tmpHdrs = [];
var msgHdr;
var numMsgs = msgbase.total_msgs;
for (var msgIdx = 0; msgIdx < numMsgs; ++msgIdx)
{
msgHdr = msgbase.get_msg_header(true, msgIdx, expandFields);
tmpHdrs.push(msgHdr);
}
msgbase.close();
}
// Filter the headers into this.hdrsForCurrentSubBoard
if (tmpHdrs != null)
this.FilterMsgHdrsIntoHdrsForCurrentSubBoard(tmpHdrs, true);
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
}
// For the DigDistMsgReader class: Takes an array of message headers in the current
// sub-board and filters them into this.hdrsForCurrentSubBoard and
// this.hdrsForCurrentSubBoardByMsgNum based on which messages are readable to the
// user.
//
// Parameters:
// pMsgHdrs: An array/object of message header objects
// pClearFirst: Boolean - Whether or not to empty this.hdrsForCurrentSubBoard
// and this.hdrsForCurrentSubBoardByMsgNum first.
function DigDistMsgReader_FilterMsgHdrsIntoHdrsForCurrentSubBoard(pMsgHdrs, pClearFirst)
{
if (pClearFirst)
{
this.hdrsForCurrentSubBoard = [];
this.hdrsForCurrentSubBoardByMsgNum = new Object();
}
for (var prop in pMsgHdrs)
{
// Only add the message header if the message is readable to the user
if (isReadableMsgHdr(pMsgHdrs[prop], this.subBoardCode))
{
this.hdrsForCurrentSubBoard.push(pMsgHdrs[prop]);
this.hdrsForCurrentSubBoardByMsgNum[pMsgHdrs[prop].number] = this.hdrsForCurrentSubBoard.length - 1;
}
}
}
// For the DigDistMsgReader class: Gets the message offset (index) for a message, given

nightfox
committed
// a message header. Returns -1 on failure. The returned index is for the object's
// message header array(s), if populated, in the priority of search headers, then
// hdrsForCurrentSubBoard. If neither of those are populated, the offset of the header
// in the messagebase will be returned.
//
// Parameters:
// pHdrOrMsgNum: Can either be a message header object or a message number.
//
// Return value: The message index (or offset in the messagebase)
function DigDistMsgReader_GetMsgIdx(pHdrOrMsgNum)
{

nightfox
committed
var msgNum = -1;
if (typeof(pHdrOrMsgNum) == "object")
msgNum = pHdrOrMsgNum.number;
else if (typeof(pHdrOrMsgNum) == "number")
msgNum = pHdrOrMsgNum;
else

nightfox
committed
return -1;

nightfox
committed
if (typeof(msgNum) != "number")
return -1;
var msgIdx = 0;
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode) &&
(this.msgSearchHdrs[this.subBoardCode].indexed.length > 0))
{
for (var i = 0; i < this.msgSearchHdrs[this.subBoardCode].indexed.length; ++i)
{
if (this.msgSearchHdrs[this.subBoardCode].indexed[i].number == msgNum)
{
msgIdx = i;
break;
}
}
}
else if (this.hdrsForCurrentSubBoard.length > 0)
{
if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(msgNum))
msgIdx = this.hdrsForCurrentSubBoardByMsgNum[msgNum];
else
msgIdx = -1;
}
else
msgIdx = msgNumToIdxFromMsgbase(this.subBoardCode, msgNum);
return msgIdx;
}
// Given a sub-board code and message number, this function gets the index
// of that message from the Synchronet messagebase. Returns -1 if not found.
//
// Parameters:
// pSubCode: The sub-board code
// pMsgNum: The message number
//
// Return value: The index of the message, or -1 if not found.
function msgNumToIdxFromMsgbase(pSubCode, pMsgNum)
{
var msgIdx = -1;
var msgbase = new MsgBase(pSubCode);
if (msgbase.open())
{
var msgHdr = msgbase.get_msg_header(false, pMsgNum, false);
if (msgHdr != null)
msgIdx = msgHdr.offset;
msgbase.close();
}
return msgIdx;
}
// For the DigDistMsgReader class: 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.
// pSubBoardCode: Optional - An internal sub-board code. If not specified, then
// this method will default to this.subBoardCode.
function DigDistMsgReader_RefreshSearchResultMsgHdr(pMsgIndex, pAttrib, pSubBoardCode)
{
if (typeof(pMsgIndex) != "number")
return;
var subCode = (typeof(pSubBoardCode) == "string" ? pSubBoardCode : this.subBoardCode);
var msgbase = new MsgBase(subCode);
if (msgbase.open())
{
if (this.msgSearchHdrs.hasOwnProperty(subCode))
{
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;
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;
msgbase.put_msg_header(true, msgHeader.offset, msgHeader);
}
}
}
msgbase.close();
}
}

nightfox
committed
// For the DigDistMsgReader class: Refreshes a message header in the message header
// array for the current sub-board.
//
// 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_RefreshHdrInSubBoardHdrs(pMsgIndex, pAttrib)
{
if (typeof(pMsgIndex) != "number")
return;

nightfox
committed
if ((pMsgIndex >= 0) && (pMsgIndex < this.hdrsForCurrentSubBoard.length))

nightfox
committed
this.hdrsForCurrentSubBoard[pMsgIndex].attr = this.hdrsForCurrentSubBoard[pMsgIndex].attr | pAttrib;
}
// For the DigDistMsgReader class: Refreshes a message header in the saved message
// header arrays.
//
// 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.
// pSubBoardCode: Optional - An internal sub-board code. If not specified, then
// this method will default to this.subBoardCode.
function DigDistMsgReader_RefreshHdrInSavedArrays(pMsgIndex, pAttrib, pSubBoardCode)
{
this.RefreshSearchResultMsgHdr(pMsgIndex, pAttrib, pSubBoardCode);
this.RefreshHdrInSubBoardHdrs(pMsgIndex, pAttrib);
}
// 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
{
//this.ReadOrListSubBoard(pSubBoardCode);
this.ReadOrListSubBoard(subCode);
// Clear the search data so that subsequent listing or reading sessions
// don't repeat the same search
this.ClearSearchData();
}
}
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
}
// 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;
}
}
// If the user doesn't have permission to read the current sub-board, then
// don't allow the user to read it.
if (this.subBoardCode != "mail")
{
if (!msg_area.sub[this.subBoardCode].can_read)
{
var errorMsg = format(bbs.text(CantReadSub), msg_area.sub[this.subBoardCode].grp_name, msg_area.sub[this.subBoardCode].name);
console.print("\1n" + errorMsg);
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;
}
// If not searching, then populate the array of all readable headers for the
// current sub-board.
if (!this.SearchingAndResultObjsDefinedForCurSub())
this.PopulateHdrsForCurrentSubBoard();
// 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.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 if (this.hdrsForCurrentSubBoard.length > 0)
{
selectedMessageOffset = this.GetMsgIdx(msg_area.sub[this.subBoardCode].scan_ptr);
if (selectedMessageOffset < 0)
selectedMessageOffset = 0;
else if (selectedMessageOffset >= this.hdrsForCurrentSubBoard.length)
selectedMessageOffset = this.hdrsForCurrentSubBoard.length - 1;
}
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.

nightfox
committed
otherRetObj = this.ReadMessages(null, selectedMessageOffset, true, 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.
// List messages
otherRetObj = this.ListMessages(null, pAllowChgArea);
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
1844
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
1872
1873
1874
1875
1876
1877
1878
// 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;
}
}
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.searchType, this.searchString, this.readingPersonalEmailFromUser);
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
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
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
}
}
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;
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();
// Create an array of internal codes of sub-boards to scan
var subBoardsToScan = [];
if (scanScopeChar == "A") // All sub-board scan
{
for (var grpIndex = 0; grpIndex < msg_area.grp_list.length; ++grpIndex)
{
for (var subIndex = 0; subIndex < msg_area.grp_list[grpIndex].sub_list.length; ++subIndex)
subBoardsToScan.push(msg_area.grp_list[grpIndex].sub_list[subIndex].code);
}
}
else if (scanScopeChar == "G") // Group scan
{
for (var subIndex = 0; subIndex < msg_area.grp_list[bbs.curgrp].sub_list.length; ++subIndex)
subBoardsToScan.push(msg_area.grp_list[bbs.curgrp].sub_list[subIndex].code);
}
else if (scanScopeChar == "S") // Current sub-board scan
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
subBoardsToScan.push(bbs.cursub_code);
// Do a scan through the sub-boards
this.doingMsgScan = true;
var continueNewScan = true;
var userAborted = false;
this.doingMultiSubBoardScan = (subBoardsToScan.length > 1);
for (var subCodeIdx = 0; (subCodeIdx < subBoardsToScan.length) && continueNewScan; ++subCodeIdx)
{
// Force garbage collection to ensure enough memory is available to continue
js.gc(true);
// 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.
this.setSubBoardCode(subBoardsToScan[subCodeIdx]); // 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.
//var msgbase = new MsgBasesubBoardsToScan[subCodeIdx]);
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
// Get a filtered list of messages for this sub-board
this.PopulateHdrsForCurrentSubBoard();
//this.setSubBoardCode(subBoardsToScan[subCodeIdx]); // Needs to be set before getting the last read/scan pointer index
// If the current sub-board contains only deleted messages,
// or if the user has already read the last message in this
// sub-board, then skip it.
var scanPtrMsgIdx = this.GetScanPtrMsgIdx();
var nonDeletedMsgsExist = (this.FindNextNonDeletedMsgIdx(scanPtrMsgIdx-1, true) > -1);
var userHasReadLastMessage = false;
if (this.subBoardCode != "mail")
{
// What if newest_message_header.number is invalid (e.g. NaN or 0xffffffff or >
// msgbase.last_msg)?
if (this.hdrsForCurrentSubBoard.length > 0)
{
if ((msg_area.sub[this.subBoardCode].last_read == this.hdrsForCurrentSubBoard[this.hdrsForCurrentSubBoard.length-1].number) ||
(scanPtrMsgIdx == this.hdrsForCurrentSubBoard.length-1))
{
userHasReadLastMessage = true;
}
}
}
if (!nonDeletedMsgsExist || userHasReadLastMessage)
{
if (msgbase != null)
msgbase.close();
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.
// 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.
var totalNumMsgs = msgbase.total_msgs;
if ((totalNumMsgs > 0) && ((scanPtrMsgIdx == -1) || (scanPtrMsgIdx < totalNumMsgs-1)))
{
bbs.curgrp = grpIndex;
bbs.cursub = subIndex;
// Start at the scan pointer
var startMsgIdx = scanPtrMsgIdx;
// If the message has already been read, then start at the next message
var tmpMsgHdr = this.GetMsgHdrByIdx(startMsgIdx);
if ((tmpMsgHdr != null) && (msg_area.sub[this.subBoardCode].last_read == tmpMsgHdr.number) && (startMsgIdx < this.NumMessages(true) - 1))
++startMsgIdx;
// 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;
}
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
}
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);
Loading
Loading full blame...