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 ...
* 2016-11-19 Eric Oulashin Version 1.17 Beta 1
* Looked into handling null message headers, which is
* more likely now with the introduction of vote messages
* in Synchronet 3.17. Headers for vote messages will
* be null. I updated the GetMsgHdrBy* methods to
* return a bogus message header if he message header
* is null. It will have the property 'isBogus'
* with the value of true. That way, the message
* list won't have any weirdness on the screen, and
* the user won't be able to read the message. I'd
* like to have the reader not display vote messages
* at all. I may need to use the get_all_msg_headers()
* method for that.
* 2016-11-20 Eric Oulashin Continued working on updates for Synchronet's
* voting support - Mainly filtering out voting
* messages etc.

nightfox
committed
* 2016-12-03 Eric Oulashin Version 1.17 beta 15
* Added a check in DigDistMsgReader_GetMsgIdx() to
* just return -1 if msgNum is not a number. Also,
* have been continuing to work on voting & message
* filtering updates.

nightfox
committed
* 2017-08-05 Eric Oulashin Version 1.17 beta 42
* When showing tally information for messages with
* upvotes & downvotes, it now shows who voted on
* the message (for sysops only).
* 2017-08-17 Eric Oulashin Version 1.17 beta 43
* Updated to support multi-answer poll voting. Works
* with Synchronet 3.17 builds from August 14, 2017 onward.

nightfox
committed
* 2017-08-19 Eric Oulashin Version 1.17 beta 45
* Used the new Msgbase.close_poll() method in the August
* 19, 2017 build of Synchronet 3.17
* 2017-09-27 Eric Oulashin Version 1.17 beta 47
* Removed this.msgbase in the DigDistMsgReader class and
* updated any functions requiring it to open the
* sub-board using a new MsgBase class with this.subBoardCode.
* This was done to (hopefully) avoid the occasional
* random crashing of Synchronet when doing things like
* replying to a message, etc.
* Updated to set the message number length field length dynamically,
* more than 4 digits if necessary.
* 2017-11-28 Eric Oulashin Version 1.17 beta 48
* Bug fix: When listing messages in reverse order in
* lightbar mode and all messages are selected/de-selected,
* the checkmarks on the screen now update properly.
* Also, updated so that when listing personal email, it
* will use the regular formatting colors rather than the
* colors for messages to the user, since all personal
* emails are to the user (the 'to user' colors for each
* email might be obnoxious).
* 2017-12-04 Eric Oulashin Version 1.17 beta 49
* Bug fixed (revealed with the 12/1/2017 Synchronet build):
* When adding the 'read' message attribute to a message
* to a user read in a new-message scan, it now uses a
* message header without expanded fields so that it can
* save properly.
* 2017-12-09 Eric Oulashin Version 1.17 beta 50
* Fixed a bug introduced in the previous version: It
* was no longer showing voting stats for messages.
* 2017-12-18 Eric Oulashin Version 1.17 beta 51
* Updated the definitions of the KEY_PAGE_UP and KEY_PAGE_DOWN
* veriables to match what they are in sbbsdefs.js (if defined)
* from December 18, 2017 so that the PageUp and PageDown keys
* continue to work properly. This script should still also
* work with older builds of Synchronet.
* 2017-12-29 Eric Oulashin Version 1.17 beta 52
* Bug fix: When using the scrolling interface to read a
* message, any message color codes that might appear in
* the message lines are preserved across lines.

nightfox
committed
* 2018-01-12 Eric Oulashin Version 1.17 beta 53
* Added support for displaying user avatars, recently
* added to Synchronet. Added the configuration option
* displayAvatars to toggle this feature.
* 2018-01-20 Eric Oulashin Version 1.17 beta 54
* Added a configuration file option, rightJustifyAvatars.
* 2018-01-27 Eric Oulashin Version 1.17 beta 55
* Added the new @-code MSG_FROM_AND_FROM_NET and
* MSG_FROM_AND_FROM_NET-L (for left-justification with
* field length), which shows the 'from' name with
* the from network in parenthesis. Updated the
* default message header to show that information.
* 2018-02-01 Eric Oulashin Version 1.17 beta 56
* When replying to a message, the reader will
* strip any control characters that might be in the
* subject line.
* 2018-02-09 Eric Oulashin Version 1.17 beta 57
* Started working on a 'bypass' (B) command while reading when
* doing a message scan/newscan.
* 2018-03-19 Eric Oulashin Made a fix for voting input - It wasn't accepting
* Q to quit out of voting (a blank input worked though).
* Releasing this, even though the 'bypass' command
* isn't finished yet.

nightfox
committed
* 2018-03-25 Eric Oulashin Version 1.17 beta 58
* For voting, updated to use BallotHdr (791)
* instead of SelectHdr (501).
* 2018-04-23 Eric Oulashin Version 1.17 beta 59
* When submitting a vote, the thread_id field is now
* set to the message/poll's message ID, not message number.
* 2018-05-08 Eric Oulashin Version 1.17 beta 60
* When a non-sysop is reading anonymous posts, the
* "from" name is now shown as "Anonymous".
* 2018-07-17 Eric Oulashin Version 1.17 beta 63
* Just before showing the message list or changing
* to another message area from the reader interface,
* it now writes "Loading..." in case there are a
* very large number of messages or sub-boards.
* 2019-01-02 Eric Oulashin Version 1.17 (non-beta)
* Removed the "beta" status after the official release
* of Synchronet 3.17b. Also, made use of
* file_cfgname() when finding the configuration file
* location.
* 2019-04-10 Eric Oulashin Version 1.18 Beta
* Started making use of the new MsgBase.get_index() function
* (if available) for better performance.
* 2019-04-15 Eric Oulashin Version 1.18
* Added 'undefined' checks for some of the messaeg attribute
* definitions before adding them to the attribute strings,
* since some of them have changed.
* Released this version as non-beta.
* 2019-04-24 Eric Oulashin Version 1.19 Beta
* Removed use of get_index(), since that method of filtering
* messages returned headers without vote information.
* 2019-04-25 Eric Oulashin Version 1.19
* Updated to show message score values in the message list
* if the terminal is wide enough (>= 86 characters)
* 2019-04-26 Eric Oulashin Version 1.20
* Added configurable options for the message score colors
* for the message list
* 2019-05-04 Eric Oulashin Version 1.21
* Started updating to use require() instead of load()
* if the require() function exists (it was added in
* Synchronet 3.17).
* 2019-05-09 Eric Oulashin Version 1.22
* When displaying the 'enhanced' header ANSI, now it checks
* to see if the total_votes and upvotes are both non-zero
* before adding score information to the header ANSI. With
* one of the Synchronet updates, it started showing the score
* for all messages, rather than only messages with the
* non-zero score information. Perhaps at some point, total_votes
* and upvotes were added to all message headers, including
* ones that had no votes?
*/
// TODO: Support anonymous posts? Bit values for sub[x].settings:
//SUB_ANON =(1<<8); // Allow anonymous posts on sub
//SUB_AONLY =(1<<9); // Anonymous only
/* 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.
*/
const requireFnExists = (typeof(require) === "function");
if (requireFnExists)
{
require("sbbsdefs.js", "K_UPPER");
require("text.js", "Email"); // Text string definitions (referencing text.dat)
}
else
{
load("sbbsdefs.js");
load("text.js"); // Text string definitions (referencing text.dat)
}
// This script requires Synchronet version 3.15 or higher.
// Exit if the Synchronet version is below the minimum.
if (system.version_num < 31500)
{
var message = "\1n\1h\1y\1i* Warning:\1n\1h\1w Digital Distortion Message Reader "
+ "requires version \1g3.15\1w or\r\n"
+ "higher of Synchronet. This BBS is using version \1g" + system.version
+ "\1w. Please notify the sysop.";
console.crlf();
console.print(message);
console.crlf();
console.pause();
exit();
}
// Reader version information
var READER_VERSION = "1.22";
var READER_DATE = "2019-05-09";
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// 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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
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;
// 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
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
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)
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
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)
{
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
// 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;
// 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;
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
// 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";