Newer
Older
/* This is a message reader/lister door for Synchronet. Features include:
* - Listing messages in the user's current message area with the ability to
* navigate forwards & backwards through the list (and for ANSI users, a
* lightbar interface will be used, or optionally can be set to use a more
* traditional interface for ANSI users)
* - The user can select a message from the list to read and optionally reply to
* - For ANSI users, reading messages is done with an enhanced user interface,
* with the ability to scroll up & down through the message, move to the next
* or previous message using the right & left arrow keys, display the message
* list to choose another message to read, etc.
* - The ability to start up with the message list or reading messages in the
* user's current message area (AKA sub-board)
* - Message searching
*
* Author: Eric Oulashin (AKA Nightfox)
* BBS: Digital Distortion
* BBS address: digitaldistortionbbs.com (or digdist.bbsindex.com)
*
* Date Author Description
* 2014-09-13 Eric Oulashin Started (based on my message lister script)
* ... Comments trimmed ...
* 2022-03-14 Eric Oulashin Version 1.47
* Updated to make DDMsgReader can be called directly as a
* loadable module by Synchronet (work started on March 8).
* Also, refactored to use attr_conv.js and removed the
* attribute conversion functions from this script.
* 2022-03-23 Eric Oulashin Version 1.47a
* Now calls bbs.edit_msg() to edit an existing message (if
* that function exists - It was added in Synchronet 3.18).
* 2022-06-12 Eric Oulashin Version 1.48
Eric Oulashin
committed
* Improved display of ANSI messages via the use of the Graphic object
* 2022-06-13 Eric Oulashin Version 1.49
* Refactor: Simplified saving a message to BBS machine for sysop
* (as-is, less processing); removed attachment stuff for pre-Synchronet
* 3.17; moved hasSyncAttrCodes() to attr_conv.js because that's where it
* needs to be.
* 2022-06-13 Eric Oulashin Version 1.50
* When doing a text search, it now ignores the user scan configuration for
* sub-boards, to ensure it will show any results of the text search.
Eric Oulashin
committed
* 2022-07-05 Eric Oulashin Version 1.51
* Graphic is now only used when using the scrollable interface. Also,
* when creating the Graphic, now subtracting 1 from the reading area height
* to avoid making the Graphic one line too tall to avoid unnecessary scrolling.
* When saving messages with ANSI codes, Graphic is only used if the message has
* any ASCII drawing characters. (not sure if this really matters much though).
* Also, applied "use strict" and made some changes as necessary.

Eric Oulashin
committed
* 2022-07-09 Eric Oulashin Version 1.52
* Mouse click support for the bottom help lines in scrollable mode
* (thanks to help from Nelgin)
* 2022-07-18 Eric Oulashin Version 1.53
* Deleted messages can now be un-marked for deletion from the message
* list with the U key (if the user has delete permissions). Also, the reader now
* honors the system setting for whether users can view deleted messages.

Eric Oulashin
committed
* 2022-08-06 Eric Oulashin Version 1.54
* Users now have a personal twit list (configurable via Ctrl-U, user settings).

Eric Oulashin
committed
* 2022-09-23 Eric Oulashin Version 1.55
* Refactored how email replies are done (passing the header to the appropriate
* functions, not using ungetstr() when prompting for the message subject)
Eric Oulashin
committed
* 2022-11-25 Eric Oulashin Version 1.56
* Fixed bug startup mode for scanning all groups for un-read messages to you where
* the reader was bringing up personal email instead.

Eric Oulashin
committed
* 2022-12-02 Eric Oulashin Version 1.57
* @-codes were only expanded when reading personal mail; now, DDMsgReader
* also checks to make sure the sender is a sysop. Also, used putmsg() in
* place of this script's own @-message parsing when displaying some of the
* configured text strings.
* 2022-12-12 Eric Oulashin Fix for "assignment to undeclared variable" error in GetMsgSubBrdLine();
* appeared when changing to a different message area from the reader
* 2012-12-14 Eric Oulashin Version 1.58
* When writing QUOTES.TXT, quote lines are now wrapped if the user's
* external editor configuration is configured to do so.

Eric Oulashin
committed
* 2022-12-29 Eric Oulashin Version 1.59
* For Synchronet above 3.20, read the external editor quote wrap setting
* from xtrn.ini. Below version 3.20, read it from xtrn.cnf.
* Also, there's a new user setting to toggle whether or not to use the scrollbar
* in the scrolling reader. Currently there is no alternate progress displayed
* if not using the scrollbar, but that is planned for a future update.

Eric Oulashin
committed
* 2023-01-20 Eric Oulashin Version 1.60
* DDMsgReader can now optionally convert Y-style MCI attribute codes to
* to Synchronet attribute codes, with the new configuration setting
* convertYStyleMCIAttrsToSync (true/false). Requires the updated attr_conv.js
* in sbbs/exec/load.
* 2023-01-22 Eric Oulashin Version 1.61
* Fix: When replying to an email with an unknown sender (empty),
* no longer gives the error "Invalid user field: 0"; also, if the sender is
* unknown, prompts the user for a user name/number/email address to send
* the reply to.
* 2023-01-30 Eric Oulashin Version 1.62
* (Hopefully) Improved display of ANSI messages which would previously look
* bad with empty lines evrey other line

Eric Oulashin
committed
* 2023-02-01 Eric Oulashin Version 1.63
* Fix for reading colors from the theme file. Also, the theme file now
* no longer needs the control character for color codes.
*/
"use strict";
// TODO: In the message list, add the ability to search with / similar to my area chooser
/* 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.
*/
// - For pageUp & pageDown, enable alternate keys:
// - When reading a message - scrollTextLines()
// - When listing messages
// - When listing message groups & sub-boards for sub-board selection
// - For sub-board area search:
// - Enable searching in traditional interface
// - Update the keys in the lightbar help line and traditional interface

Eric Oulashin
committed
// This script requires Synchronet version 3.18 or higher (for mouse hotspot support).
// Exit if the Synchronet version is below the minimum.

Eric Oulashin
committed
if (system.version_num < 31800)
{
var message = "\x01n\x01h\x01y\x01i* Warning:\x01n\x01h\x01w Digital Distortion Message Reader "

Eric Oulashin
committed
+ "requires version \x01g3.18\x01w or\r\n"
+ "higher of Synchronet. This BBS is using version \x01g" + system.version
+ "\x01w. Please notify the sysop.";
console.crlf();
console.print(message);
console.crlf();
console.pause();
exit();
}
Eric Oulashin
committed
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");
require("dd_lightbar_menu.js", "DDLightbarMenu");
require("html2asc.js", 'html2asc');
require("attr_conv.js", "convertAttrsToSyncPerSysCfg");
require("graphic.js", 'Graphic');
load('822header.js');
var ansiterm = require("ansiterm_lib.js", 'expand_ctrl_a');
// Reader version information

Eric Oulashin
committed
var READER_VERSION = "1.63";
var READER_DATE = "2023-02-01";
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// 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

Eric Oulashin
committed
var MID_BLOCK = "\xDC";
var TALL_UPPER_MID_BLOCK = "\xFE";
var UPPER_CENTER_BLOCK = "\xDF";
var LOWER_CENTER_BLOCK = "\xDC";
const ERROR_PAUSE_WAIT_MS = 1500;
// 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;
// Message list sort types
const MSG_LIST_SORT_DATETIME_RECEIVED = 0;
const MSG_LIST_SORT_DATETIME_WRITTEN = 1;
// 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 = {
MSG_DELETE: "Del",
MSG_PRIVATE: "Priv",
MSG_READ: "Read",
MSG_PERMANENT: "Perm",
MSG_LOCKED: "Lock",
MSG_ANONYMOUS: "Anon",
MSG_KILLREAD: "Killread",
MSG_MODERATED: "Mod",
MSG_VALIDATED: "Valid",
MSG_REPLIED: "Repl",
MSG_NOREPLY: "NoRepl"
};
var gAuxMsgAttrStrs = {
MSG_FILEREQUEST: "Freq",
MSG_FILEATTACH: "Attach",
MSG_KILLFILE: "KillFile",
MSG_RECEIPTREQ: "RctReq",
MSG_CONFIRMREQ: "ConfReq",
MSG_NODISP: "NoDisp"
};
if (typeof(MSG_TRUNCFILE) != "undefined")
gAuxMsgAttrStrs.MSG_TRUNCFILE = "TruncFile";
var gNetMsgAttrStrs = {
MSG_LOCAL: "FromLocal",
MSG_INTRANSIT: "Transit",
MSG_SENT: "Sent",
MSG_KILLSENT: "KillSent",
MSG_ARCHIVESENT: "ArcSent",
MSG_HOLD: "Hold",
MSG_CRASH: "Crash",
MSG_IMMEDIATE: "Now",
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);

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"))
{
Eric Oulashin
committed
require("smbdefs.js", "SMB_POLL_ANSWER");

nightfox
committed
gAvatar = load({}, "avatar_lib.js");
}

Eric Oulashin
committed
// User twitlist filename (and settings filename)
var gUserTwitListFilename = backslash(system.data_dir + "user") + format("%04d", user.number) + ".DDMsgReader_twitlist";

Eric Oulashin
committed
var gUserSettingsFilename = backslash(system.data_dir + "user") + format("%04d", user.number) + ".DDMsgReader_Settings";

Eric Oulashin
committed
/////////////////////////////////////////////
// Script execution code
// Parse the command-line arguments
var gCmdLineArgVals = parseArgs(argv);
if (gCmdLineArgVals.exitNow)
exit(0);
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
if (gCmdLineArgVals.hasOwnProperty("search") && (gCmdLineArgVals["search"].toLowerCase() == "prompt"))
{
console.print("\x01n");
console.crlf();
console.print("\x01cMessage search:");
console.crlf();
var allowedKeys = "";
if (!gListPersonalEmailCmdLineOpt)
{
allowedKeys = "ANKFTYUS";
console.print(" \x01g\x01hN\x01y = \x01n\x01cNew message scan");
console.crlf();
console.print(" \x01g\x01hK\x01y = \x01n\x01cKeyword");
console.crlf();
console.print(" \x01h\x01gF\x01y = \x01n\x01cFrom name");
console.crlf();
console.print(" \x01h\x01gT\x01y = \x01n\x01cTo name");
console.crlf();
console.print(" \x01h\x01gY\x01y = \x01n\x01cTo you");
console.crlf();
console.print(" \x01h\x01gU\x01y = \x01n\x01cUnread (new) messages to you");
console.crlf();
console.print(" \x01h\x01gS\x01y = \x01n\x01cScan for msgs to you");
console.crlf();
}
else
{
// Reading personal email - Allow fewer choices
allowedKeys = "KF";
console.print(" \x01g\x01hK\x01y = \x01n\x01cKeyword");
console.crlf();
console.print(" \x01h\x01gF\x01y = \x01n\x01cFrom name");
console.crlf();
}
console.print(" \x01h\x01gA\x01y = \x01n\x01cAbort");
console.crlf();
console.print("\x01n\x01cMake a selection\x01g\x01h: \x01c");
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
// 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("\x01n\x01h\x01y\x01iAborted\x01n");
console.crlf();
console.pause();
break;
}
}

nightfox
committed
if (gDoDDMR)
{

Eric Oulashin
committed
// Write the user's default twitlist if it doesn't already exist
writeDefaultUserTwitListIfNotExist();
// When exiting this script, make sure to set the ctrl key pasthru back to what it was originally
js.on_exit("console.ctrlkey_passthru = " + console.ctrlkey_passthru);

Eric Oulashin
committed
// Set a control key pass-thru so we can capture certain control keys that we normally wouldn't be able to
var gOldCtrlKeyPassthru = console.ctrlkey_passthru; // Backup to be restored later
console.ctrlkey_passthru = "+ACGKLOPQRTUVWXYZ_";
// 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);

Eric Oulashin
committed
if (gCmdLineArgVals.indexedmode)
{

Eric Oulashin
committed
msgReader.DoIndexedMode();
}

Eric Oulashin
committed
else
{

Eric Oulashin
committed
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
// 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)
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;
if (msgReader.subBoardCode != "mail")
{
console.print("\x01n");
console.crlf();
console.print("Loading " + subBoardGrpAndName(msgReader.subBoardCode) + "....");
console.line_counter = 0; // To prevent a pause before the message list comes up
}
msgReader.ReadOrListSubBoard();
break;
case SEARCH_KEYWORD:
var txtToSearch = (gCmdLineArgVals.hasOwnProperty("searchtext") ? gCmdLineArgVals.searchtext : null);
var subBoardCode = (gCmdLineArgVals.hasOwnProperty("subboard") ? gCmdLineArgVals.subboard : null);
msgReader.SearchMsgScan("keyword_search", txtToSearch, subBoardCode);
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();

Eric Oulashin
committed
console.putmsg(msgReader.text.newMsgScanText);

Eric Oulashin
committed
}
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();

Eric Oulashin
committed
console.putmsg(msgReader.text.newToYouMsgScanText);

Eric Oulashin
committed
}
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();

Eric Oulashin
committed
console.putmsg(msgReader.text.allToYouMsgScanText);

Eric Oulashin
committed
}
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.)

Eric Oulashin
committed
console.attributes = "N";
}
// End of script execution. Functions below:
// Generates an internal enhanced reader header line for the 'To' user.
//
// Parameters:
// pColors: A JSON object containing the color settings read from the
// theme configuration file. This function will use the
// 'msgHdrToColor' or 'msgHdrToUserColor' property, depending
// on the pToReadingUser property.
// pToReadingUser: Boolean - Whether or not to generate the line with
// the color/attribute for the reading user
//
// Return value: A string containing the internal enhanced reader header
// line specifying the 'to' user
function genEnhHdrToUserLine(pColors, pToReadingUser)
{
var toHdrLine = "\x01n\x01h\x01k" + VERTICAL_SINGLE + BLOCK1 + BLOCK2 + BLOCK3
+ "\x01gT\x01n\x01go \x01h\x01c: " +
(pToReadingUser ? pColors.msgHdrToUserColor : pColors.msgHdrToColor) +
"@MSG_TO-L";
var numChars = console.screen_columns - 21;
for (var i = 0; i < numChars; ++i)
toHdrLine += "#";
toHdrLine += "@\x01k" + VERTICAL_SINGLE;
return toHdrLine;
}
///////////////////////////////////////////////////////////////////////////////////
// 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)
{
// 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
Eric Oulashin
committed
this.SearchMsgScan = DigDistMsgReader_SearchMsgScan;
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.CreateLightbarMsgListMenu = DigDistMsgReader_CreateLightbarMsgListMenu;
this.CreateLightbarMsgGrpMenu = DigDistMsgReader_CreateLightbarMsgGrpMenu;
this.CreateLightbarSubBoardMenu = DigDistMsgReader_CreateLightbarSubBoardMenu;
this.AdjustLightbarMsgListMenuIdxes = DigDistMsgReader_AdjustLightbarMsgListMenuIdxes;
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;

Eric Oulashin
committed
this.ScrollableReaderNextReadableMessage = DigDistMsgReader_ScrollableReaderNextReadableMessage;
this.ScrollReaderDetermineClickCoordAction = DigDistMsgReader_ScrollReaderDetermineClickCoordAction;
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.EditExistingMessageOldWay = DigDistMsgReader_EditExistingMessageOldWay;
this.CanDelete = DigDistMsgReader_CanDelete;
this.CanDeleteLastMsg = DigDistMsgReader_CanDeleteLastMsg;
this.CanEdit = DigDistMsgReader_CanEdit;
this.CanQuote = DigDistMsgReader_CanQuote;
this.ReadConfigFile = DigDistMsgReader_ReadConfigFile;

Eric Oulashin
committed
this.ReadUserSettingsFile = DigDistMsgReader_ReadUserSettingsFile;

Eric Oulashin
committed
this.WriteUserSettingsFile = DigDistMsgReader_WriteUserSettingsFile;

Eric Oulashin
committed
// TODO: Is this.DisplaySyncMsgHeader even needed anymore? Looks like it's not being called.
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
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.PromptAndDeleteOrUndeleteMessage = DigDistMsgReader_PromptAndDeleteOrUndeleteMessage;
this.PromptAndDeleteOrUndeleteSelectedMessages = DigDistMsgReader_PromptAndDeleteOrUndeleteSelectedMessages;
this.GetExtdMsgHdrInfo = DigDistMsgReader_GetExtdMsgHdrInfo;
this.GetMsgInfoForEnhancedReader = DigDistMsgReader_GetMsgInfoForEnhancedReader;
this.GetLastReadMsgIdxAndNum = DigDistMsgReader_GetLastReadMsgIdxAndNum;
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.DeleteOrUndeleteSelectedMessages = DigDistMsgReader_DeleteOrUndeleteSelectedMessages;
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 = [];
// hdrsForCurrentSubBoardByMsgNum is an object that maps absolute message numbers
// to their index to hdrsForCurrentSubBoard
this.hdrsForCurrentSubBoardByMsgNum = {};
// 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 = {};
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);

Eric Oulashin
committed
//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);
this.SUBJ_LEN = 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
// 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"));

Eric Oulashin
committed
if (this.showScoresInMsgList)
this.SUBJ_LEN -= (this.SCORE_LEN + 1); // + 1 to account for a space
// 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;
// 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 prompts & functions.
this.text = {
scrollbarBGChar: BLOCK1,
scrollbarScrollBlockChar: BLOCK2,
goToPrevMsgAreaPromptText: "\x01n\x01c\x01hGo to the previous message area",
goToNextMsgAreaPromptText: "\x01n\x01c\x01hGo to the next message area",
newMsgScanText: "\x01c\x01hN\x01n\x01cew \x01hM\x01n\x01cessage \x01hS\x01n\x01ccan",
newToYouMsgScanText: "\x01c\x01hN\x01n\x01cew \x01hT\x01n\x01co \x01hY\x01n\x01cou \x01hM\x01n\x01cessage \x01hS\x01n\x01ccan",
allToYouMsgScanText: "\x01c\x01hA\x01n\x01cll \x01hM\x01n\x01cessages \x01hT\x01n\x01co \x01hY\x01n\x01cou \x01hS\x01n\x01ccan",
goToMsgNumPromptText: "\x01n\x01cGo to message # (or \x01hENTER\x01n\x01c to cancel)\x01g\x01h: \x01c",
msgScanAbortedText: "\x01n\x01h\x01cM\x01n\x01cessage scan \x01h\x01y\x01iaborted\x01n",
deleteMsgNumPromptText: "\x01n\x01cNumber of the message to be deleted (or \x01hENTER\x01n\x01c to cancel)\x01g\x01h: \x01c",
editMsgNumPromptText: "\x01n\x01cNumber of the message to be edited (or \x01hENTER\x01n\x01c to cancel)\x01g\x01h: \x01c",
searchingSubBoardAbovePromptText: "\x01n\x01cSearching (current sub-board: \x01b\x01h%s\x01n\x01c)",
searchingSubBoardText: "\x01n\x01cSearching \x01h%s\x01n\x01c...",
noMessagesInSubBoardText: "\x01n\x01h\x01bThere are no messages in the area \x01w%s\x01b.",
noSearchResultsInSubBoardText: "\x01n\x01h\x01bNo messages were found in the area \x01w%s\x01b with the given search criteria.",
msgScanCompleteText: "\x01n\x01h\x01cM\x01n\x01cessage scan complete\x01h\x01g.\x01n",
invalidMsgNumText: "\x01n\x01y\x01hInvalid message number: %d",
readMsgNumPromptText: "\x01n\x01g\x01h\x01i* \x01n\x01cRead message #: \x01h",
msgHasBeenDeletedText: "\x01n\x01h\x01g* \x01yMessage #\x01w%d \x01yhas been deleted.",
noKludgeLinesForThisMsgText: "\x01n\x01h\x01yThere are no kludge lines for this message.",
searchingPersonalMailText: "\x01w\x01hSearching personal mail\x01n",
searchTextPromptText: "\x01cEnter the search text\x01g\x01h:\x01n\x01c ",
fromNamePromptText: "\x01cEnter the 'from' name to search for\x01g\x01h:\x01n\x01c ",
toNamePromptText: "\x01cEnter the 'to' name to search for\x01g\x01h:\x01n\x01c ",
abortedText: "\x01n\x01y\x01h\x01iAborted\x01n",
loadingPersonalMailText: "\x01n\x01cLoading %s...",
msgDelConfirmText: "\x01n\x01h\x01yDelete\x01n\x01c message #\x01h%d\x01n\x01c: Are you sure",
msgUndelConfirmText: "\x01n\x01h\x01yUndelete\x01n\x01c message #\x01h%d\x01n\x01c: Are you sure",
delSelectedMsgsConfirmText: "\x01n\x01h\x01yDelete selected messages: Are you sure",
undelSelectedMsgsConfirmText: "\x01n\x01h\x01yUndelete selected messages: Are you sure",
msgDeletedText: "\x01n\x01cMessage #\x01h%d\x01n\x01c has been marked for deletion.",
msgUndeletedText: "\x01n\x01cMessage #\x01h%d\x01n\x01c has been unmarked for deletion.",
selectedMsgsDeletedText: "\x01n\x01cSelected messages have been marked for deletion.",
selectedMsgsUndeletedText: "\x01n\x01cSelected messages have been unmarked for deletion.",
cannotDeleteMsgText_notYoursNotASysop: "\x01n\x01h\x01wCannot delete message #\x01y%d \x01wbecause it's not yours or you're not a sysop.",
cannotDeleteMsgText_notLastPostedMsg: "\x01n\x01h\x01g* \x01yCannot delete message #%d. You can only delete your last message in this area.\x01n",
cannotDeleteAllSelectedMsgsText: "\x01n\x01y\x01h* Cannot delete all selected messages",
msgEditConfirmText: "\x01n\x01cEdit message #\x01h%d\x01n\x01c: Are you sure",
noPersonalEmailText: "\x01n\x01cYou have no messages.",
postOnSubBoard: "\x01n\x01gPost on %s %s"
};
// 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
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
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",

Eric Oulashin
committed
userSettings: CTRL_U,
threadView: "*" // TODO: Implement this
};
Eric Oulashin
committed
if (user.is_sysop)
this.enhReaderKeys.validateMsg = "A";
// Some key bindings for the message list (not necessarily all of them)
this.msgListKeys = {
deleteMessage: KEY_DEL,
undeleteMessage: "U",
batchDelete: CTRL_D,
editMsg: "E",
goToMsg: "G",
chgMsgArea: "C",

Eric Oulashin
committed
userSettings: CTRL_U,
quit: "Q",
showHelp: "?"
};

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

nightfox
committed
// Message list sort option
this.msgListSort = MSG_LIST_SORT_DATETIME_RECEIVED;

Eric Oulashin
committed
// Whether or not to convert Y-style MCI attribute codes to Synchronet attribute codes
this.convertYStyleMCIAttrsToSync = false;

Eric Oulashin
committed
// Whether or not to use the scrollbar in the enhanced message reader
this.useEnhReaderScrollbar = true;
this.cfgFilename = "DDMsgReader.cfg";
// Check the command-line arguments for a custom configuration file name
// before reading the configuration file.
var scriptArgsIsValid = (typeof(pScriptArgs) == "object");
if (scriptArgsIsValid && pScriptArgs.hasOwnProperty("configfilename"))
this.cfgFilename = pScriptArgs["configfilename"];
// Read the settings from the config file
this.cfgFileSuccessfullyRead = false;
this.ReadConfigFile();

Eric Oulashin
committed
this.userSettings = {
twitList: []
};

Eric Oulashin
committed
this.ReadUserSettingsFile(false);
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
// 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 = "\x01n" + this.colors.msgListMsgNumHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListFromHighlightColor = "\x01n" + this.colors.msgListFromHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListToHighlightColor = "\x01n" + this.colors.msgListToHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListSubjHighlightColor = "\x01n" + this.colors.msgListSubjHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListDateHighlightColor = "\x01n" + this.colors.msgListDateHighlightColor + this.colors.msgListHighlightBkgColor;
this.colors.msgListTimeHighlightColor = "\x01n" + this.colors.msgListTimeHighlightColor + this.colors.msgListHighlightBkgColor;
// Similar for the area chooser lightbar highlight colors
this.colors.areaChooserMsgAreaNumHighlightColor = "\x01n" + this.colors.areaChooserMsgAreaNumHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaDescHighlightColor = "\x01n" + this.colors.areaChooserMsgAreaDescHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaDateHighlightColor = "\x01n" + this.colors.areaChooserMsgAreaDateHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaTimeHighlightColor = "\x01n" + this.colors.areaChooserMsgAreaTimeHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
this.colors.areaChooserMsgAreaNumItemsHighlightColor = "\x01n" + this.colors.areaChooserMsgAreaNumItemsHighlightColor + this.colors.areaChooserMsgAreaBkgHighlightColor;
// Similar for the enhanced reader help line colors
this.colors.enhReaderHelpLineGeneralColor = "\x01n" + this.colors.enhReaderHelpLineGeneralColor + this.colors.enhReaderHelpLineBkgColor;
this.colors.enhReaderHelpLineHotkeyColor = "\x01n" + this.colors.enhReaderHelpLineHotkeyColor + this.colors.enhReaderHelpLineBkgColor;
this.colors.enhReaderHelpLineParenColor = "\x01n" + this.colors.enhReaderHelpLineParenColor + this.colors.enhReaderHelpLineBkgColor;
// Similar for the lightbar message list help line colors
this.colors.lightbarMsgListHelpLineGeneralColor = "\x01n" + this.colors.lightbarMsgListHelpLineGeneralColor + this.colors.lightbarMsgListHelpLineBkgColor;
this.colors.lightbarMsgListHelpLineHotkeyColor = "\x01n" + this.colors.lightbarMsgListHelpLineHotkeyColor + this.colors.lightbarMsgListHelpLineBkgColor;
this.colors.lightbarMsgListHelpLineParenColor = "\x01n" + this.colors.lightbarMsgListHelpLineParenColor + this.colors.lightbarMsgListHelpLineBkgColor;
// Similar for the lightbar area chooser help line colors
this.colors.lightbarAreaChooserHelpLineGeneralColor = "\x01n" + this.colors.lightbarAreaChooserHelpLineGeneralColor + this.colors.lightbarAreaChooserHelpLineBkgColor;
this.colors.lightbarAreaChooserHelpLineHotkeyColor = "\x01n" + this.colors.lightbarAreaChooserHelpLineHotkeyColor + this.colors.lightbarAreaChooserHelpLineBkgColor;
this.colors.lightbarAreaChooserHelpLineParenColor = "\x01n" + 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) != "\x01n"))
this.text[prop] = "\x01n" + 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);
// this.enhMsgHeaderLinesToReadingUser will be a copy of this.endMsgReaderLines
// but with the 'To' user line changed to highlight the name for messages to
// the logged-on reading user
this.enhMsgHeaderLinesToReadingUser = [];
// 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)
{
// 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 = "\x01n\x01h\x01c" + UPPER_LEFT_SINGLE + HORIZONTAL_SINGLE + "\x01n\x01c"
+ HORIZONTAL_SINGLE + " \x01h@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 += "@\x01k";
numChars = console.screen_columns - console.strlen(hdrLine1) - 4;
for (var i = 0; i < numChars; ++i)
hdrLine1 += HORIZONTAL_SINGLE;
hdrLine1 += "\x01n\x01c" + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\x01h"
+ HORIZONTAL_SINGLE + UPPER_RIGHT_SINGLE;
this.enhMsgHeaderLines.push(hdrLine1);
this.enhMsgHeaderLinesToReadingUser.push(hdrLine1);
var hdrLine2 = "\x01n\x01c" + VERTICAL_SINGLE + "\x01h\x01k" + BLOCK1 + BLOCK2
+ BLOCK3 + "\x01gM\x01n\x01gsg#\x01h\x01c: " + this.colors.msgHdrMsgNumColor + "@MSG_NUM_AND_TOTAL-L";
numChars = console.screen_columns - 32;
for (var i = 0; i < numChars; ++i)
hdrLine2 += "#";
hdrLine2 += "@\x01n\x01c" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine2);
this.enhMsgHeaderLinesToReadingUser.push(hdrLine2);
var hdrLine3 = "\x01n\x01h\x01k" + VERTICAL_SINGLE + BLOCK1 + BLOCK2 + BLOCK3
+ "\x01gF\x01n\x01grom\x01h\x01c: " + this.colors.msgHdrFromColor + "@MSG_FROM_AND_FROM_NET-L";
numChars = console.screen_columns - 36;
for (var i = 0; i < numChars; ++i)
hdrLine3 += "#";
hdrLine3 += "@\x01k" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine3);
this.enhMsgHeaderLinesToReadingUser.push(hdrLine3);
this.enhMsgHeaderLines.push(genEnhHdrToUserLine(this.colors, false));
this.enhMsgHeaderLinesToReadingUser.push(genEnhHdrToUserLine(this.colors, true));
var hdrLine5 = "\x01n\x01h\x01k" + VERTICAL_SINGLE + BLOCK1 + BLOCK2 + BLOCK3
+ "\x01gS\x01n\x01gubj\x01h\x01c: " + this.colors.msgHdrSubjColor + "@MSG_SUBJECT-L";
numChars = console.screen_columns - 26;
for (var i = 0; i < numChars; ++i)
hdrLine5 += "#";
hdrLine5 += "@\x01k" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine5);
this.enhMsgHeaderLinesToReadingUser.push(hdrLine5);
var hdrLine6 = "\x01n\x01c" + VERTICAL_SINGLE + "\x01h\x01k" + BLOCK1 + BLOCK2 + BLOCK3
+ "\x01gD\x01n\x01gate\x01h\x01c: " + this.colors.msgHdrDateColor + "@MSG_DATE-L";
//numChars = console.screen_columns - 23;
numChars = console.screen_columns - 67;
for (var i = 0; i < numChars; ++i)
hdrLine6 += "#";
//hdrLine6 += "@\x01n\x01c" + VERTICAL_SINGLE;
hdrLine6 += "@ @MSG_TIMEZONE@\x01n";
for (var i = 0; i < 40; ++i)
hdrLine6 += " ";
hdrLine6 += "\x01n\x01c" + VERTICAL_SINGLE;
this.enhMsgHeaderLines.push(hdrLine6);
this.enhMsgHeaderLinesToReadingUser.push(hdrLine6);
var hdrLine7 = "\x01n\x01h\x01c" + BOTTOM_T_SINGLE + HORIZONTAL_SINGLE + "\x01n\x01c"
+ HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\x01h\x01k";
numChars = console.screen_columns - 8;
for (var i = 0; i < numChars; ++i)
hdrLine7 += HORIZONTAL_SINGLE;
hdrLine7 += "\x01n\x01c" + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + "\x01h"
+ HORIZONTAL_SINGLE + BOTTOM_T_SINGLE;
this.enhMsgHeaderLines.push(hdrLine7);
this.enhMsgHeaderLinesToReadingUser.push(hdrLine7);
}
else
{
// We loaded the enhanced message header lines from a custom file.
// Copy from this.enhMsgHeaderLines to this.enhMsgHeaderLinesToReadingUser
// but change any 'To:' line to highlight the 'to' username.
this.enhMsgHeaderLinesToReadingUser = this.enhMsgHeaderLines.slice();
// Go through the header lines and ensure the 'To' line has a different
// color
for (var lineIdx = 0; lineIdx < this.enhMsgHeaderLinesToReadingUser.length; ++lineIdx)
this.enhMsgHeaderLinesToReadingUser[lineIdx] = syncAttrCodesToANSI(strWithToUserColor(this.enhMsgHeaderLinesToReadingUser[lineIdx], this.colors.msgHdrToUserColor));
}
// 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;
}
}
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
// 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.WriteGrpListHdrLine1 = DigDistMsgReader_WriteGrpListTopHdrLine1;
this.WriteSubBrdListHdrLine = DigDistMsgReader_WriteSubBrdListHdrLine;
this.SelectMsgArea = DigDistMsgReader_SelectMsgArea;
this.SelectMsgArea_Lightbar = DigDistMsgReader_SelectMsgArea_Lightbar;
this.SelectMsgArea_Traditional = DigDistMsgReader_SelectMsgArea_Traditional;
this.ListMsgGrps = DigDistMsgReader_ListMsgGrps_Traditional;
this.ListSubBoardsInMsgGroup = DigDistMsgReader_ListSubBoardsInMsgGroup_Traditional;
// Lightbar-specific methods
this.WriteMsgGroupLine = DigDistMsgReader_writeMsgGroupLine;
this.UpdateMsgAreaPageNumInHeader = DigDistMsgReader_updateMsgAreaPageNumInHeader;
this.GetMsgSubBoardLine = DigDistMsgReader_GetMsgSubBrdLine;
// 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;

Eric Oulashin
committed
this.DoUserSettings_Scrollable = DigDistMsgReader_DoUserSettings_Scrollable;
this.DoUserSettings_Traditional = DigDistMsgReader_DoUserSettings_Traditional;
this.RefreshMsgAreaRectangle = DigDistMsgReader_RefreshMsgAreaRectangle;
this.MsgHdrFromOrToInUserTwitlist = DigDistMsgReader_MsgHdrFromOrToInUserTwitlist;
// For indexed mode
this.DoIndexedMode = DigDistMsgReader_DoIndexedMode;
this.DoIndexedModeLightbar = DigDistMsgReader_DoIndexedModeLightbar;
this.DoIndexedModeTraditional = DigDistMsgReader_DoIndexedModeTraditional;
this.MakeLightbarIndexedModeMenu = DigDistMsgReader_MakeLightbarIndexedModeMenu;
// printf strings for message group/sub-board lists
// Message group information (printf strings)
this.msgGrpListPrintfStr = "\x01n " + this.colors.areaChooserMsgAreaNumColor + "%" + this.areaNumLen
+ "d " + this.colors.areaChooserMsgAreaDescColor + "%-"
+ this.msgGrpDescLen + "s " + this.colors.areaChooserMsgAreaNumItemsColor
+ "%" + this.numItemsLen + "d";
this.msgGrpListHilightPrintfStr = "\x01n" + this.colors.areaChooserMsgAreaBkgHighlightColor + " "
+ "\x01n" + this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaNumHighlightColor + "%" + this.areaNumLen
+ "d \x01n" + this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaDescHighlightColor + "%-"
+ this.msgGrpDescLen + "s \x01n" + 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

Eric Oulashin
committed
// For PageUp, normally I'd think KEY_PAGEUP should work, but that triggers sending a telegram instead. \x1b[V seems to work though.
this.lightbarAreaChooserHelpLine = "\x01n"

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@CLEAR_HOT@@`" + UP_ARROW + "`" + KEY_UP + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`" + DOWN_ARROW + "`" + KEY_DOWN + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`HOME`" + KEY_HOME + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`END`" + KEY_END + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "#"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`PgUp`" + "\x1b[V" + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "/"

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`Dn`" + KEY_PAGEDN + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`F`F@"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "irst pg, "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`L`L@"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "ast pg, "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`CTRL-F`" + CTRL_F + "@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`/`/@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`N`N@"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + ", "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`Q`Q@"
+ this.colors.lightbarAreaChooserHelpLineParenColor + ")"
+ this.colors.lightbarAreaChooserHelpLineGeneralColor + "uit, "

Eric Oulashin
committed
+ this.colors.lightbarAreaChooserHelpLineHotkeyColor + "@`?`?@";
var lbAreaChooserHelpLineLen = 72;
// 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)

Eric Oulashin
committed
var padLen = console.screen_columns - lbAreaChooserHelpLineLen - 1;
var leftPadLen = Math.floor(padLen/2);

Eric Oulashin
committed
var rightPadLen = padLen - leftPadLen;
this.lightbarAreaChooserHelpLine = this.colors.lightbarAreaChooserHelpLineGeneralColor
+ format("%" + leftPadLen + "s", "")
+ this.lightbarAreaChooserHelpLine
+ this.colors.lightbarAreaChooserHelpLineGeneralColor
+ format("%" + rightPadLen + "s", "") + "\x01n";
// 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 = [];
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
// 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 = {};
// 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 = "\x01n\x01c[ 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 = {};
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.

Eric Oulashin
committed
// Note: get_all_msg_headers() was added in Synchronet 3.16. DDMsgReader requires a minimum
// of 3.18, so we're okay to use it.
// The first parameter is whether to include votes (the parameter was introduced in Synchronet 3.17+).
// We used to pass false here.
tmpHdrs = msgbase.get_all_msg_headers(true);
msgbase.close();
}
// Filter the headers into this.hdrsForCurrentSubBoard
this.FilterMsgHdrsIntoHdrsForCurrentSubBoard(tmpHdrs, true);
}
// 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 = {};
}
if (pMsgHdrs == null)
return;
for (var prop in pMsgHdrs)
{

Eric Oulashin
committed
// Only add the message header if the message is readable to the user
// and the from & to name isn't in the user's personal twitlist.
// this.hdrsForCurrentSubBoardByMsgNum also has to be populated, but
// that's done later in this function, in case this.hdrsForCurrentSubBoard
// needs to be sorted.

Eric Oulashin
committed
if (isReadableMsgHdr(pMsgHdrs[prop], this.subBoardCode) && !this.MsgHdrFromOrToInUserTwitlist(pMsgHdrs[prop]))
{
this.hdrsForCurrentSubBoard.push(pMsgHdrs[prop]);
// This isn't done right here anymore due to the possibility of
// this.hdrsForCurrentSubBoard being sorted
//this.hdrsForCurrentSubBoardByMsgNum[pMsgHdrs[prop].number] = this.hdrsForCurrentSubBoard.length - 1;
}
}
// If the sort type is date/time written, then sort the message header
// array as such
if (this.msgListSort == MSG_LIST_SORT_DATETIME_WRITTEN)
this.hdrsForCurrentSubBoard.sort(sortMessageHdrsByDateTime);
// Populate this.hdrsForCurrentSubBoardByMsgNum (this needs to be done here
// based on the order of this.hdrsForCurrentSubBoard)
for (var idx = 0; idx < this.hdrsForCurrentSubBoard.length; ++idx)
this.hdrsForCurrentSubBoardByMsgNum[this.hdrsForCurrentSubBoard[idx].number] = idx;
}
// 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 = msgNumToIdxFromMsgbase(this.subBoardCode, msgNum);
if (msgIdx != -1)
this.hdrsForCurrentSubBoardByMsgNum[msgNum] = msgIdx;
}
}
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.
// pApply: Optional boolean - Whether or not to apply the attribute or remove it. Defaults to true.
// pSubBoardCode: Optional - An internal sub-board code. If not specified, then
// this method will default to this.subBoardCode.
function DigDistMsgReader_RefreshSearchResultMsgHdr(pMsgIndex, pAttrib, pApply, pSubBoardCode)
{
if (typeof(pMsgIndex) != "number")
return;
var applyAttr = (typeof(pApply) === "boolean" ? pApply : true);
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))
{
if (applyAttr)
this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex].attr = this.msgSearchHdrs[this.subBoardCode].indexed[pMsgIndex].attr | pAttrib;
else
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.
// pApply: Optional boolean - Whether or not to apply the attribute or remove it. Defaults to true.
function DigDistMsgReader_RefreshHdrInSubBoardHdrs(pMsgIndex, pAttrib, pApply)

nightfox
committed
{
if (typeof(pMsgIndex) != "number")
return;

nightfox
committed
if ((pMsgIndex >= 0) && (pMsgIndex < this.hdrsForCurrentSubBoard.length))
{
var applyAttr = (typeof(pApply) === "boolean" ? pApply : true);
if (applyAttr)
this.hdrsForCurrentSubBoard[pMsgIndex].attr = this.hdrsForCurrentSubBoard[pMsgIndex].attr | pAttrib;
else
this.hdrsForCurrentSubBoard[pMsgIndex].attr = this.hdrsForCurrentSubBoard[pMsgIndex].attr ^ pAttrib;
}

nightfox
committed
}
// 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.
// pApply: Optional boolean - Whether or not to apply the attribute or remove it. Defaults to true.
// pSubBoardCode: Optional - An internal sub-board code. If not specified, then
// this method will default to this.subBoardCode.
function DigDistMsgReader_RefreshHdrInSavedArrays(pMsgIndex, pAttrib, pApply, pSubBoardCode)

nightfox
committed
{
var applyAttr = (typeof(pApply) === "boolean" ? pApply : true);
this.RefreshSearchResultMsgHdr(pMsgIndex, pAttrib, applyAttr, pSubBoardCode);
this.RefreshHdrInSubBoardHdrs(pMsgIndex, pAttrib, applyAttr);

nightfox
committed
}
// For the DigDistMsgReader class: Inputs search text from the user, then reads/lists
// messages, which will perform the search.
//
// Paramters:
Eric Oulashin
committed
// 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"
Eric Oulashin
committed
// for personal email. Or, this can be the a boolean false for scan
// search mode to scan through sub-boards while searching each of them.
// pScanScopeChar: Optional string with a character specifying "A" to scan all sub-boards,
// "G" for the current message group, or "S" for the user's current sub-board.
// If this is not specified, the current sub-board will be used.
// pTxtToSearch: Optional - Text to search for (if specified, this won't prompt the user for search text)
// pSkipSubBoardScanCfgCheck: Optional boolean - Whether or not to skip the sub-board scan config check for
// each sub-board. Defaults to false.
function DigDistMsgReader_SearchMessages(pSearchModeStr, pSubBoardCode, pScanScopeChar, pTxtToSearch, pSkipSubBoardScanCfgCheck)
{
var searchTextProvided = (typeof(pTxtToSearch) === "string" && pTxtToSearch != "");
var skipSubBoardScanCfgCheck = (typeof(pSkipSubBoardScanCfgCheck) === "boolean" ? pSkipSubBoardScanCfgCheck : false);
// 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("\x01n");
console.crlf();
Eric Oulashin
committed
var subCode = "";
if (typeof(pScanScopeChar) !== "string")
{
subCode = (typeof(pSubBoardCode) === "string" ? pSubBoardCode : this.subBoardCode);
if (subCode == "mail")

Eric Oulashin
committed
{
//console.print("\x01n" + replaceAtCodesInStr(this.text.searchingPersonalMailText));
console.putmsg("\x01n" + this.text.searchingPersonalMailText);
}
Eric Oulashin
committed
else
{
var formattedText = format(this.text.searchingSubBoardAbovePromptText, subBoardGrpAndName(bbs.cursub_code));

Eric Oulashin
committed
//console.print("\x01n" + replaceAtCodesInStr(formattedText) + "\x01n");
console.putmsg("\x01n" + formattedText + "\x01n");
Eric Oulashin
committed
}
console.crlf();
}
// Output the prompt text to the user (for modes where a prompt is needed)
switch (this.searchType)
{
case SEARCH_KEYWORD:
if (!searchTextProvided)

Eric Oulashin
committed
{
//console.print("\x01n" + replaceAtCodesInStr(this.text.searchTextPromptText));
console.putmsg("\x01n" + this.text.searchTextPromptText);
}
console.print("\x01n\x01gSearching for: \x01c" + pTxtToSearch + "\x01n\r\n");
break;
case SEARCH_FROM_NAME:
if (!searchTextProvided)

Eric Oulashin
committed
{
//console.print("\x01n" + replaceAtCodesInStr(this.text.fromNamePromptText));
console.putmsg("\x01n" + this.text.fromNamePromptText);
}
console.print("\x01n\x01gSearching for: \x01c" + pTxtToSearch + "\x01n\r\n");
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
if (!searchTextProvided)

Eric Oulashin
committed
{
//console.print("\x01n" + replaceAtCodesInStr(this.text.toNamePromptText));
console.putmsg("\x01n" + this.text.toNamePromptText);
}
console.print("\x01n\x01gSearching for: \x01c" + pTxtToSearch + "\x01n\r\n");
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)
{
if (searchTextProvided)
this.searchString = pTxtToSearch;
else
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();

Eric Oulashin
committed
//console.print("\x01n" + replaceAtCodesInStr(this.text.abortedText));
//console.crlf();
console.putmsg("\x01n" + this.text.abortedText);
console.pause();
return;
}
else
{
Eric Oulashin
committed
// If pScanScopeChar is a string, then do scan search mode. Otherwise,
// scan/search the current sub-board.
if (typeof(pScanScopeChar) === "string" && (pScanScopeChar === "S" || pScanScopeChar === "G" || pScanScopeChar === "A"))
{
var subBoardCodeBackup = this.subBoardCode;
var subBoardsToScan = getSubBoardsToScanArray(pScanScopeChar);
this.doingMsgScan = true;
var continueScan = true;
var userAborted = false;
this.doingMultiSubBoardScan = (subBoardsToScan.length > 1);
Eric Oulashin
committed
// 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.
Eric Oulashin
committed
for (var subCodeIdx = 0; (subCodeIdx < subBoardsToScan.length) && continueScan; ++subCodeIdx)
{
subCode = subBoardsToScan[subCodeIdx];
if (skipSubBoardScanCfgCheck || (msg_area.sub[subCode].can_read && ((msg_area.sub[subCode].scan_cfg & SCAN_CFG_NEW) == SCAN_CFG_NEW)))
Eric Oulashin
committed
{
// 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;
Eric Oulashin
committed
// let the user read the sub-board (and toggle betweeen reading and
// listing)
var readOrListRetObj = this.ReadOrListSubBoard(subCode, null, true, true, false, true, READER_MODE_READ);
console.print("\x01n");
Eric Oulashin
committed
console.crlf();
//if (this.SearchTypePopulatesSearchResults())
// console.print("\x01n\r\nSearching...");
Eric Oulashin
committed
console.line_counter = 0;
if (readOrListRetObj.stoppedReading)
break;
}
}
this.subBoardCode = subBoardCodeBackup;
Eric Oulashin
committed
console.pause();
Eric Oulashin
committed
}
else
this.ReadOrListSubBoard(subCode);
// Clear the search data so that subsequent listing or reading sessions
// don't repeat the same search
this.ClearSearchData();
}
}
}
Eric Oulashin
committed
// For the DigDistMsgReader class: Performs a message search scan through sub-boards.
// Prompts the user for Sub-board/Group/All, then inputs search text from the user, then
// reads/lists messages through the sub-boards, performing the search in each sub-board.
//
// 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
// pTxtToSearch: Optional - Text to search for (if specified, this won't prompt the user for search text)
// pSubCode: Optional - An internal code of a sub-board if scanning just one sub-board
function DigDistMsgReader_SearchMsgScan(pSearchModeStr, pTxtToSearch, pSubCode)
Eric Oulashin
committed
{
if (typeof(pSearchModeStr) !== "string" || pSearchModeStr.length == 0)
return;
// If the given sub-board code is valid, then use that and scan only in that
// sub-board. Otherwise, prompt the user for sub-board, group, or all, then
// call SearchMessages to do the search.
var scanScopeChar = "";
var previousSubBoardCode = null;
if (typeof(pSubCode) === "string" && subBoardCodeIsValid(pSubCode))
{
var previousSubBoardCode = this.subBoardCode;
this.subBoardCode = pSubCode;
scanScopeChar = "S";
}
else
{
console.mnemonics(bbs.text(SubGroupOrAll));
scanScopeChar = console.getkeys("SGAC").toString();
}
Eric Oulashin
committed
if (scanScopeChar.length > 0)
this.SearchMessages(pSearchModeStr, null, scanScopeChar, pTxtToSearch, true); // Skip/ignore scan config checks
Eric Oulashin
committed
else
{
console.crlf();

Eric Oulashin
committed
//console.print(replaceAtCodesInStr(this.text.msgScanAbortedText));
//console.crlf();
console.putmsg(this.text.msgScanAbortedText);
Eric Oulashin
committed
console.pause();
}
// Restore this.subBoardCode if necessary
if (typeof(previousSubBoardCode) === "string")
this.subBoardCode = previousSubBoardCode;
Eric Oulashin
committed
}
// 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 = {};
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
}
}
// 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.
Eric Oulashin
committed
// pPromptToGoNextIfNoResults: Optional boolean - Whether or not to prompt the user
// to go onto the next/previous sub-board if there are no
// search results in the current sub-board. Defaults to true.
Eric Oulashin
committed
// pInitialModeOverride: Optional (numeric) to override the initial mode in this
// function (READER_MODE_READ or READER_MODE_LIST). If not
// specified, defaults to this.startMode.
//
// 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,
Eric Oulashin
committed
pPauseOnNoMsgSrchResults,
Eric Oulashin
committed
pPromptToGoNextIfNoResults,
pInitialModeOverride)
{
var 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("\x01n\x01h\x01yWarning: \x01wThe 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("\x01n" + 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;
Eric Oulashin
committed
// If an initial mode override was specified and is valid, then use it.
if (typeof(pInitialModeOverride) === "number" && (pInitialModeOverride == READER_MODE_READ || pInitialModeOverride == READER_MODE_LIST))
readerMode = pInitialModeOverride;
// 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.GetLastReadMsgIdxAndNum(false).lastReadMsgIdx; // Used to be true
if ((selectedMessageOffset > -1) && (selectedMessageOffset < this.NumMessages() - 1))
++selectedMessageOffset;
}
else
selectedMessageOffset = 0;
}
else if (this.hdrsForCurrentSubBoard.length > 0)
{
selectedMessageOffset = this.GetMsgIdx(GetScanPtrOrLastMsgNum(this.subBoardCode));
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.
Eric Oulashin
committed
otherRetObj = this.ReadMessages(null, selectedMessageOffset, true, allowChgMsgArea,
pReturnOnNextAreaNav, pPromptToGoNextIfNoResults);
// 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;
Eric Oulashin
committed
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);
// 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("\x01n");
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
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();
Eric Oulashin
committed
var formattedText = "";
if (this.readingPersonalEmail)
Eric Oulashin
committed
formattedText = format(this.text.loadingPersonalMailText, subBoardGrpAndName(this.subBoardCode));
else
Eric Oulashin
committed
formattedText = format(this.text.searchingSubBoardText, subBoardGrpAndName(this.subBoardCode));

Eric Oulashin
committed
//console.print("\x01n" + replaceAtCodesInStr(formattedText) + "\x01n");

Eric Oulashin
committed
console.putmsg("\x01n" + formattedText + "\x01n");
}
this.msgSearchHdrs[this.subBoardCode] = searchMsgbase(this.subBoardCode, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
}
}
else
{
// There is no search is specified, so clear the search results for the
// current sub-board to help ensure that there are messages to read.
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode))
{
delete this.msgSearchHdrs[this.subBoardCode].indexed;
delete this.msgSearchHdrs[this.subBoardCode];
}
}
// If there are no messages to display in the current sub-board, then set the
// return value and let the user know (if outputMessages is true).
if (this.NumMessages() == 0)
{
thereAreMessagesToRead = false;
if (outputMessages)
{
console.print("\x01n");
console.crlf();
if (this.readingPersonalEmail)

Eric Oulashin
committed
{
//console.print(replaceAtCodesInStr(this.text.noPersonalEmailText));
console.putmsg(this.text.noPersonalEmailText);
}
else
{
Eric Oulashin
committed
var formattedText = "";
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode))
Eric Oulashin
committed
formattedText = format(this.text.noSearchResultsInSubBoardText, subBoardGrpAndName(this.subBoardCode));
else
Eric Oulashin
committed
formattedText = format(this.text.noMessagesInSubBoardText, subBoardGrpAndName(this.subBoardCode));

Eric Oulashin
committed
//console.print(replaceAtCodesInStr(formattedText));
console.putmsg(formattedText);
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
}
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
Eric Oulashin
committed
// 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("\x01n");
Eric Oulashin
committed
console.mnemonics(bbs.text(SubGroupOrAll));
scanScopeChar = console.getkeys("SGAC").toString();
// If the user just pressed Enter without choosing anything, then abort and return.
if (scanScopeChar.length == 0)
{
console.crlf();

Eric Oulashin
committed
//console.print(replaceAtCodesInStr(this.text.msgScanAbortedText));
//console.crlf();
console.putmsg(this.text.msgScanAbortedText);
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
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
Eric Oulashin
committed
var subBoardsToScan = getSubBoardsToScanArray(scanScopeChar);
// Scan through the sub-boards
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
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))
{
Eric Oulashin
committed
var grpIndex = msg_area.sub[this.subBoardCode].grp_index;
var subIndex = msg_area.sub[this.subBoardCode].index;
// 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;
}
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
}
break;
case SCAN_TOYOU: // All messages to the user
bbs.curgrp = grpIndex;
bbs.cursub = subIndex;
// Search for messages to the user in the current sub-board
// and let the user read the sub-board if messages are
// found. Don't allow the user to change to a different
// message area, don't pause when there's no search results
// in a sub-board, and return instead of going to the next
// sub-board via navigation.
this.searchType = SEARCH_TO_USER_CUR_MSG_AREA;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
break;
case SCAN_UNREAD: // New (unread) messages to the user
bbs.curgrp = grpIndex;
bbs.cursub = subIndex;
// Search for messages to the user in the current sub-board
// and let the user read the sub-board if messages are
// found. Don't allow the user to change to a different
// message area, don't pause when there's no search results
// in a sub-board, and return instead of going to the next
// sub-board via navigation.
this.searchType = SEARCH_TO_USER_NEW_SCAN;
var readRetObj = this.ReadOrListSubBoard(null, 0, false, true, false);
// If the user stopped reading & decided to quit, then exit the
// message scan loops.
if (readRetObj.stoppedReading)
{
continueNewScan = false;
userAborted = true;
}
break;
default:
break;
}
if (msgbase != null)
msgbase.close();
}
}
// Pause for a short moment to avoid causing CPU usage going to 99%
mswait(10);
}
this.doingMultiSubBoardScan = false;
// Restore the original sub-board code, searched message headers, etc.
this.searchType = originalSearchType;
this.setSubBoardCode(originalSubBoardCode);
this.msgSearchHdrs = originalMsgSrchHdrs;
bbs.curgrp = originalBBSCurGrp;
bbs.cursub = originalBBSCurSub;
if ((msgbase != null) && msgbase.is_open)
msgbase.close();
this.doingMultiSubBoardScan = false;
this.doingMsgScan = false;
if (this.pauseAfterNewMsgScan)
{
console.crlf();
if (userAborted)

Eric Oulashin
committed
{
//console.print("\x01n" + replaceAtCodesInStr(this.text.msgScanAbortedText) + "\x01n");
console.putmsg("\x01n" + this.text.msgScanAbortedText + "\x01n");
}
else

Eric Oulashin
committed
{
//console.print("\x01n" + replaceAtCodesInStr(this.text.msgScanCompleteText) + "\x01n");
console.putmsg("\x01n" + this.text.msgScanCompleteText + "\x01n");
}
console.crlf();
console.pause();
}
}
// For the DigDistMsgReader class: Performs the message reading activity.
//
// Parameters:
// pSubBoardCode: Optional - The internal code of a sub-board to read.
// If not specified, the internal sub-board code specified
// when creating the object will be used.
// pStartingMsgOffset: Optional - The offset of a message to start at
// pReturnOnMessageList: Optional boolean - Whether or not to quit when the
// user wants to list messages (used when this method
// is called from ReadOrListSubBoard()).
// pAllowChgArea: Optional boolean - Whether or not to allow changing the
// message area
// pReturnOnNextAreaNav: Optional boolean - Whether or not this method should
// return when it would move to the next message area due
// navigation from the user (i.e., with the right arrow
// key or with < (go to previous message area) or > (go
// to next message area))
Eric Oulashin
committed
// pPromptToGoToNextAreaIfNoSearchResults: Optional boolean - Whether or not to
// prompt the user to go to the next/previous sub-board
// when there are no search results
//
// Return value: An object that has the following properties:
// lastUserInput: The user's last keypress/input
// lastAction: The last action chosen by the user based on their
// last keypress, etc.
// stoppedReading: Boolean - Whether reading has stopped
// (due to user quitting, error, or otherwise)
// messageListReturn: Boolean - Whether this method is returning for
// the caller to display the message list. This
// will only be true when the pReturnOnMessageList
// parameter is true and the user wants to list
// messages.
function DigDistMsgReader_ReadMessages(pSubBoardCode, pStartingMsgOffset, pReturnOnMessageList,
Eric Oulashin
committed
pAllowChgArea, pReturnOnNextAreaNav, pPromptToGoToNextAreaIfNoSearchResults)
{
var retObj = {
lastUserInput: "",
lastAction: ACTION_NONE,
stoppedReading: false,
messageListReturn: false
};
// If the passed-in sub-board code was different than what was set in the object before,
// then open the new message sub-board.
var previousSubBoardCode = this.subBoardCode;
if (typeof(pSubBoardCode) == "string")
{
if (subBoardCodeIsValid(pSubBoardCode))
this.setSubBoardCode(pSubBoardCode);
else
{
console.print("\x01n\x01h\x01yWarning: \x01wThe Message Reader connot continue because an invalid");
console.crlf();
console.print("sub-board code was specified (" + pSubBoardCode + "). Please notify the sysop.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
}
if (this.subBoardCode.length == 0)
{
console.print("\x01n\x01h\x01yWarning: \x01wThe Message Reader connot continue because no message");
console.crlf();
console.print("sub-board was specified. Please notify the sysop.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
// If 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("\x01n" + errorMsg);
console.pause();
retObj.stoppedReading = true;
return retObj;
}
}
var msgbase = new MsgBase(this.subBoardCode);
// If the message base was not opened, then output an error and return.
if (!msgbase.open())
{
console.print("\x01n");
console.crlf();
console.print("\x01h\x01y* \x01wUnable to open message sub-board:");
console.crlf();
console.print(subBoardGrpAndName(this.subBoardCode));
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
// If there are no messages to display in the current sub-board, then let the
// user know and exit.
var numOfMessages = this.NumMessages(msgbase);
msgbase.close();
if (numOfMessages == 0)
{
console.clear("\x01n");
console.center("\x01n\x01h\x01yThere are no messages to display.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
// Check the pAllowChgArea parameter. If it's a boolean, then use it. If
// not, then check to see if we're reading personal mail - If not, then allow
// the user to change to a different message area.
var allowChgMsgArea = true;
if (typeof(pAllowChgArea) == "boolean")
allowChgMsgArea = pAllowChgArea;
else
allowChgMsgArea = (this.subBoardCode != "mail");
// If reading personal email and messages haven't been collected (searched)
// yet, then do so now.
if (this.readingPersonalEmail && (!this.msgSearchHdrs.hasOwnProperty(this.subBoardCode)))
this.msgSearchHdrs[this.subBoardCode] = searchMsgbase(this.subBoardCode, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
// Determine the index of the message to start at. This will be
// pStartingMsgOffset if pStartingMsgOffset is valid, or the index
// of the user's last-read message in this sub-board.
var msgIndex = 0;
if ((typeof(pStartingMsgOffset) == "number") && (pStartingMsgOffset >= 0) && (pStartingMsgOffset < this.NumMessages()))
msgIndex = pStartingMsgOffset;
else if (this.SearchingAndResultObjsDefinedForCurSub())
msgIndex = 0;
else
{
msgIndex = this.GetLastReadMsgIdxAndNum().lastReadMsgIdx;
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
if (msgIndex == -1)
msgIndex = 0;
}
// If the current message index is for a message that has been
// deleted, then find the next non-deleted message.
var testMsgHdr = this.GetMsgHdrByIdx(msgIndex);
if ((testMsgHdr == null) || ((testMsgHdr.attr & MSG_DELETE) == MSG_DELETE))
{
// First try going forward
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(msgIndex, true);
// If a non-deleted message was not found, then try going backward.
if (nonDeletedMsgIdx == -1)
nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(msgIndex, false);
// If a non-deleted message was found, then set msgIndex to it.
// Otherwise, tell the user there are no messages in this sub-board
// and return.
if (nonDeletedMsgIdx > -1)
msgIndex = nonDeletedMsgIdx;
else
{
console.clear("\x01n");
console.center("\x01h\x01yThere are no messages to display.");
console.crlf();
console.pause();
retObj.stoppedReading = true;
return retObj;
}
}
// Construct the hotkey help line (needs to be done after the message
// base is open so that the delete & edit keys can be added correctly).
this.SetEnhancedReaderHelpLine();
// Get the screen ready for reading messages - First, clear the screen.
console.clear("\x01n");
// Display the help line at the bottom of the screen
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Input loop
var msgHdr = null;
var dateTimeStr = null;
var screenY = 1; // For screen updates requiring more than one line
var continueOn = true;
var readMsgRetObj = null;
// previousNextAction will store the next action from the previous iteration.
// It is useful for some checks, such as when the current message is deleted,
// we'll want to see if the user wanted to go to the previous message/area
// for navigation purposes.
var previousNextAction = ACTION_NONE;
while (continueOn && (msgIndex >= 0) && (msgIndex < this.NumMessages()))
{
// Display the message with the enhanced read method
readMsgRetObj = this.ReadMessageEnhanced(msgIndex, allowChgMsgArea);
retObj.lastUserInput = readMsgRetObj.lastKeypress;
retObj.lastAction = readMsgRetObj.nextAction;
// If we should refresh the enhanced reader help line on the screen (and
// the returned message offset is valid and the user's terminal supports ANSI),
// then refresh the help line.
if (readMsgRetObj.refreshEnhancedRdrHelpLine && readMsgRetObj.offsetValid && console.term_supports(USER_ANSI))
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// If the returned message offset is invalid, then quit.
if (!readMsgRetObj.offsetValid)
{
continueOn = false;
retObj.stoppedReading = true;
break;
}

nightfox
committed
// If the message is not readable to the user, then go to the
// next/previous message

nightfox
committed
else if (readMsgRetObj.msgNotReadable)
{
// If the user's next action in the last iteration was to go to the
// previous message, then go backwards; otherwise, go forward.
if (previousNextAction == ACTION_GO_PREVIOUS_MSG)

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

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

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

nightfox
committed
msgIndex = readMsgRetObj.newMsgOffset;
else
{
// The user is at the beginning of the current sub-board.
if (allowChgMsgArea)
{
Eric Oulashin
committed
if (this.SearchTypePopulatesSearchResults())
console.print("\x01n\r\nLoading messages...");
Eric Oulashin
committed
var goToPrevRetval = this.GoToPrevSubBoardForEnhReader(allowChgMsgArea, pPromptToGoToNextAreaIfNoSearchResults);
retObj.stoppedReading = goToPrevRetval.shouldStopReading;
// If we're going to stop reading, then
if (retObj.stoppedReading)
msgIndex = 0;
else if (goToPrevRetval.changedMsgArea)

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

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

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

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

nightfox
committed
return retObj;
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
}
}
else if (readMsgRetObj.nextAction == ACTION_GO_FIRST_MSG) // Go to the first message
{
// Go to the first message that's not marked as deleted. This passes -1 as the
// starting message index because FindNextNonDeletedMsgIdx() will increment it
// before searching in order to find the "next" message.
msgIndex = this.FindNextNonDeletedMsgIdx(-1, true);
}
else if (readMsgRetObj.nextAction == ACTION_GO_LAST_MSG) // Go to the last message
{
// Go to the last message that's not marked as deleted
msgIndex = this.FindNextNonDeletedMsgIdx(this.NumMessages(), false);
}
else if (readMsgRetObj.nextAction == ACTION_CHG_MSG_AREA) // Change message area, if allowed
{
if (allowChgMsgArea)
{
// Change message sub-board. If a different sub-board was
// chosen, then change some variables to use the new
// chosen sub-board.
var oldSubBoardCode = this.subBoardCode;
this.SelectMsgArea();
if (this.subBoardCode != oldSubBoardCode)
this.PopulateHdrsForCurrentSubBoard();
var chgSubBoardRetObj = this.EnhancedReaderChangeSubBoard(bbs.cursub_code);
if (chgSubBoardRetObj.succeeded)
{
// Set the message index, etc.
// If there are search results, then set msgIndex to the first
// message. Otherwise (if there is no search specified), then
// set the message index to the user's last read message.
if (this.SearchingAndResultObjsDefinedForCurSub())

nightfox
committed
msgIndex = 0;
else

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

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

nightfox
committed
msgIndex = nonDeletedMsgIdx;
else
{
if (this.NumMessages() != 0)
{
// There are messages, but none that are not deleted.
console.clear("\x01n");
console.center("\x01h\x01yThere are no messages to display.");
console.crlf();
console.pause();
}
retObj.stoppedReading = true;
return retObj;
}
}
// Set the hotkey help line again, since the new sub-board might have
// different settings for whether messages can be edited or deleted,
// then refresh it on the screen.
var oldHotkeyHelpLine = this.enhReadHelpLine;
this.SetEnhancedReaderHelpLine();
if ((oldHotkeyHelpLine != this.enhReadHelpLine) && this.scrollingReaderInterface && console.term_supports(USER_ANSI))

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

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

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

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

nightfox
committed
return retObj;
}
else if (readMsgRetObj.nextAction == ACTION_DISPLAY_MSG_LIST) // Display message list
{
// If we need to return to the caller for this, then do so.
if (pReturnOnMessageList)
{
retObj.messageListReturn = true;
return retObj;
}
else
{
// If this.reverseListOrder is the string "ASK", the user will be prompted
// on the last line of the screen for whether they want to list the
// messages in reverse order. So, erase the help line on the bottom of
// the screen.
if ((typeof(this.reverseListOrder) == "string") && (this.reverseListOrder.toUpperCase() == "ASK"))
{
if (this.scrollingReaderInterface && console.term_supports(USER_ANSI))
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol("\x01n");
}
}
// List messages
var listRetObj = this.ListMessages(null, pAllowChgArea);
// If the user wants to quit, then stop the input loop.
if (listRetObj.lastUserInput == "Q")
{
continueOn = false;
retObj.stoppedReading = true;
}
// If the user chose a different message, then set the message index
else if ((listRetObj.selectedMsgOffset > -1) && (listRetObj.selectedMsgOffset < this.NumMessages()))

nightfox
committed
msgIndex = listRetObj.selectedMsgOffset;
}
}
// Go to specific message & new message offset is valid: Read the new
// message
else if ((readMsgRetObj.nextAction == ACTION_GO_SPECIFIC_MSG) && (readMsgRetObj.newMsgOffset > -1))
msgIndex = readMsgRetObj.newMsgOffset;
// Save this iteration's next action for the "previous" next action for the next iteration
previousNextAction = readMsgRetObj.nextAction;
}
return retObj;
}
// For the DigDistMsgReader class: Performs the message listing, given a
// sub-board code.
//
// Paramters:
// pSubBoardCode: Optional - The internal sub-board code, or "mail"
// for personal email.

nightfox
committed
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
// Return value: An object containing the following properties:
// lastUserInput: The user's last keypress/input
// selectedMsgOffset: The index of the message selected to read,
// if one was selected. If none was selected,
// this will be -1.
function DigDistMsgReader_ListMessages(pSubBoardCode, pAllowChgSubBoard)
{
var retObj = {
lastUserInput: "",
selectedMsgOffset: -1
};
// If the passed-in sub-board code was different than what was set in the object before,
// then open the new message sub-board.
var previousSubBoardCode = this.subBoardCode;
if (typeof(pSubBoardCode) == "string")
{
if (subBoardCodeIsValid(pSubBoardCode))
this.setSubBoardCode(pSubBoardCode);
else
{
console.print("\x01n\x01h\x01yWarning: \x01wThe Message Reader connot continue because an invalid");
console.crlf();
console.print("sub-board code was specified (" + pSubBoardCode + "). Please notify the sysop.");
console.crlf();
console.pause();
return retObj;
}
}
if (this.subBoardCode.length == 0)
{
console.print("\x01n\x01h\x01yWarning: \x01wThe Message Reader connot continue because no message\r\n");
console.print("sub-board was specified. Please notify the sysop.\r\n\x01p");
// 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("\x01n" + errorMsg);
console.pause();
return retObj;
}
}
// If there are no messages to display in the current sub-board, then let the
// user know and exit.
if (this.NumMessages() == 0)
{
console.clear("\x01n");
console.center("\x01n\x01h\x01yThere are no messages to display.\r\n\x01p");
return retObj;
// Construct the traditional UI pause text and the line of help text for lightbar
// mode. This adds the delete and edit keys if the user is allowed to delete & edit
// messages.
this.SetMsgListPauseTextAndLightbarHelpLine();
// If this.reverseListOrder is the string "ASK", prompt the user for whether
// they want to list the messages in reverse order.
if ((typeof(this.reverseListOrder) == "string") && (this.reverseListOrder.toUpperCase() == "ASK"))
{
if (numMessages(bbs.cursub_code) > 0)
this.reverseListOrder = !console.noyes("\x01n\x01cList in reverse (newest on top)");
// List the messages using the lightbar or traditional interface, depending on
// what this.msgListUseLightbarListInterface is set to. The lightbar interface requires ANSI.
if (this.msgListUseLightbarListInterface && canDoHighASCIIAndANSI())
retObj = this.ListMessages_Lightbar(pAllowChgSubBoard);
else
retObj = this.ListMessages_Traditional(pAllowChgSubBoard);
return retObj;
}
// For the DigDistMsgReader class: Performs the message listing, given a
// sub-board code. This version uses a traditional user interface, prompting
// the user at the end of each page to continue, quit, or read a message.
//
// Parameters:

nightfox
committed
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
//
// Return value: An object containing the following properties:
// lastUserInput: The user's last keypress/input
// selectedMsgOffset: The index of the message selected to read,
// if one was selected. If none was selected,
// this will be -1.
function DigDistMsgReader_ListMessages_Traditional(pAllowChgSubBoard)
{
var retObj = {
lastUserInput: "",
selectedMsgOffset: -1
};
// 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("\x01n" + errorMsg);
console.pause();
return retObj;
}
}
// Reset this.readAMessage and deniedReadingmessage to false, in case the
// message listing has previously ended with them set to true.
this.readAMessage = false;
this.deniedReadingMessage = false;
var msgbase = new MsgBase(this.subBoardCode);
if (!msgbase.open())
{
console.center("\x01n\x01h\x01yError: \x01wUnable to open the sub-board.\r\n\x01p");

nightfox
committed
return retObj;
}

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

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

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

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

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

nightfox
committed
else
retvalObj = this.PromptContinueOrReadMsg((this.tradListTopMsgIdx == 0), lastScreen, allowChgSubBoard);

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

nightfox
committed
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
{
this.tradListTopMsgIdx += this.tradMsgListNumLines;
// If we go past the beginning, then we need to reset
// msgNum so we'll be at the beginning of the list.
var totalNumMessages = this.NumMessages();
if (this.tradListTopMsgIdx >= totalNumMessages)
this.tradListTopMsgIdx = totalNumMessages - 1;
}
else
{
this.tradListTopMsgIdx -= this.tradMsgListNumLines;
// If we go past the beginning, then we need to reset
// msgNum so we'll be at the beginning of the list.
if (this.tradListTopMsgIdx < 0)
this.tradListTopMsgIdx = 0;
}
}
// If the user chose to go to the next page, update
// this.tradListTopMsgIdx appropriately.
else if (retvalObj.userInput == "N")
{
if (this.reverseListOrder)

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

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

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

Eric Oulashin
committed
else if (retvalObj.userInput == "D" || retvalObj.userInput == this.msgListKeys.deleteMessage || retvalObj.userInput == '\x7f' || retvalObj.userInput == '\x08')

nightfox
committed
{

Eric Oulashin
committed
if (retvalObj.userInput == '\x08')
console.crlf();

nightfox
committed
if (this.CanDelete() || this.CanDeleteLastMsg())
{
Eric Oulashin
committed
var msgNum = this.PromptForMsgNum({ x: curpos.x, y: curpos.y+1 }, replaceAtCodesInStr(this.text.deleteMsgNumPromptText), false, ERROR_PAUSE_WAIT_MS, false);

nightfox
committed
// If the user enters a valid message number, then call the
// DeleteMessage() method, which will prompt the user for
// confirmation and delete the message if confirmed.
if (msgNum > 0)
this.PromptAndDeleteOrUndeleteMessage(msgNum-1, null, true);

nightfox
committed
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\x01n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
}
}
// E: Edit a message

Eric Oulashin
committed
else if (retvalObj.userInput == this.msgListKeys.editMsg) // "E"

nightfox
committed
{
if (this.CanEdit())
{
Eric Oulashin
committed
var msgNum = this.PromptForMsgNum({ x: curpos.x, y: curpos.y+1 }, replaceAtCodesInStr(this.text.editMsgNumPromptText), false, ERROR_PAUSE_WAIT_MS, false);

nightfox
committed
// If the user entered a valid message number, then let the
// user edit the message.
if (msgNum > 0)
{
// See if the current message header has our "isBogus" property and it's true.
// Only let the user edit the message if it's not a bogus message header.
// The message header could have the "isBogus" property, for instance, if
// it's a vote message (introduced in Synchronet 3.17).
var tmpMsgHdr = this.GetMsgHdrByIdx(msgNum-1);
var hdrIsBogus = (tmpMsgHdr.hasOwnProperty("isBogus") ? tmpMsgHdr.isBogus : false);
if (!hdrIsBogus)
var returnObj = this.EditExistingMsg(msgNum-1);
else
{
console.print("\x01n\r\n\x01h\x01yThat message isn't editable.\n");
console.crlf();
console.pause();
}
}

nightfox
committed
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\x01n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
}
}
// G: Go to a specific message by # (place that message on the top)
else if (retvalObj.userInput == "G")
{
var msgNum = this.PromptForMsgNum(curpos, "\x01n" + replaceAtCodesInStr(this.text.goToMsgNumPromptText), false, ERROR_PAUSE_WAIT_MS, false);

nightfox
committed
if (msgNum > 0)
this.tradListTopMsgIdx = msgNum - 1;

nightfox
committed
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\x01n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
}
// ?: Display help
else if (retvalObj.userInput == "?")
{
console.clear("\x01n");

nightfox
committed
this.DisplayMsgListHelp(allowChgSubBoard, true);
console.clear("\x01n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
}
// C: Change to another message area (sub-board)

Eric Oulashin
committed
else if (retvalObj.userInput == this.msgListKeys.chgMsgArea) // "C"

nightfox
committed
{
if (allowChgSubBoard && (this.subBoardCode != "mail"))
{
// Store the current sub-board code so we can see if it changed
var oldSubCode = bbs.cursub_code;
// Let the user choose another message area. If they chose
// a different message area, then set up the message base
// object accordingly.
this.SelectMsgArea();
if (bbs.cursub_code != oldSubCode)
{
var chgSubRetval = this.ChangeSubBoard(bbs.cursub_code);
continueOn = chgSubRetval.succeeded;
}
// Update the traditional list variables and refresh the screen
if (continueOn)
{
this.SetUpTraditionalMsgListVars();
console.clear("\x01n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
}
}
}
// S: Select message(s)
else if (retvalObj.userInput == "S")
{
// Input the message number list from the user
console.print("\x01n\x01cNumber(s) of message(s) to select, (\x01hA\x01n\x01c=All, \x01hN\x01n\x01c=None, \x01hENTER\x01n\x01c=cancel)\x01g\x01h: \x01c");
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
var userNumberList = console.getstr(128, K_UPPER);
// If the user entered A or N, then select/un-select all messages.
// Otherwise, select only the messages that the user entered.
if ((userNumberList == "A") || (userNumberList == "N"))
{
var messageSelectToggle = (userNumberList == "A");
var totalNumMessages = this.NumMessages();
for (var msgIdx = 0; msgIdx < totalNumMessages; ++msgIdx)
this.ToggleSelectedMessage(this.subBoardCode, msgIdx, messageSelectToggle);
}
else
{
if (userNumberList.length > 0)
{
var numArray = parseNumberList(userNumberList);
for (var numIdx = 0; numIdx < numArray.length; ++numIdx)
this.ToggleSelectedMessage(this.subBoardCode, numArray[numIdx]-1);
}
}
// Refresh the top header on the screen for continuing to list
// messages.
console.clear("\x01n");
this.WriteMsgListScreenTopHeader();
}
// Ctrl-D: Batch delete (for selected messages)

Eric Oulashin
committed
else if (retvalObj.userInput == this.msgListKeys.batchDelete) // CTRL_D
{
console.print("\x01n");
console.crlf();
if (this.NumSelectedMessages() > 0)
{
// The PromptAndDeleteOrUndeleteSelectedMessages() method will prompt the user for confirmation
// to delete the message and then delete it if confirmed.
this.PromptAndDeleteOrUndeleteSelectedMessages(null, true);
// In case all messages were deleted, if the user can't view deleted messages,
// show an appropriate message and don't continue listing messages.
//if (this.NumMessages(true) == 0)
if (!this.NonDeletedMessagesExist() && !canViewDeletedMsgs())
{
continueOn = false;
// Note: The following doesn't seem to be necessary, since
// the ReadOrListSubBoard() method will show a message saying
// there are no messages to read and then will quit out.
//msgbase.close();
//msgbase = null;
//console.clear("\x01n");
//console.center("\x01n\x01h\x01yThere are no messages to display.");
//console.crlf();
//console.pause();
}
else
{
// There are still messages to list, so refresh the top
// header on the screen for continuing to list messages.
console.clear("\x01n");
this.WriteMsgListScreenTopHeader();
}
}
else
{
// There are no selected messages
console.print("\x01n\x01h\x01yThere are no selected messages.");
mswait(ERROR_PAUSE_WAIT_MS);
// Refresh the top header on the screen for continuing to list messages.
console.clear("\x01n");
this.WriteMsgListScreenTopHeader();
}
}

Eric Oulashin
committed
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
// User settings
else if (retvalObj.userInput == this.msgListKeys.userSettings)
{
/*
var continueOn = true;
var retvalObj = null;
var curpos = null; // Current character position
var lastScreen = false;
this.RecalcMsgListWidthsAndFormatStrs();
if (this.tradListTopMsgIdx == -1)
this.SetUpTraditionalMsgListVars();
this.WriteMsgListScreenTopHeader();
*/
var userSettingsRetObj = this.DoUserSettings_Traditional();
retvalObj.userInput = "";
//drawMenu = userSettingsRetObj.needWholeScreenRefresh;
// In case the user changed their twitlist, re-filter the messages for this sub-board
if (userSettingsRetObj.userTwitListChanged)
{
console.gotoxy(1, console.screen_rows);
console.crlf();
console.print("\x01nTwitlist changed; re-filtering..");
var tmpMsgbase = new MsgBase(this.subBoardCode);
if (tmpMsgbase.open())
{
var tmpAllMsgHdrs = tmpMsgbase.get_all_msg_headers(true);
tmpMsgbase.close();
this.FilterMsgHdrsIntoHdrsForCurrentSubBoard(tmpAllMsgHdrs, true);
}
else
console.print("\x01y\x01hFailed to open the messagbase!\x01\r\n\x01p");
this.RecalcMsgListWidthsAndFormatStrs();
if (this.tradListTopMsgIdx == -1)
this.SetUpTraditionalMsgListVars();
// If there are still messages in this sub-board, and the message offset is beyond the last
// message, then adjust the top message index as necessary.
if (this.hdrsForCurrentSubBoard.length > 0)
{
if (this.tradListTopMsgIdx > this.hdrsForCurrentSubBoard.length)
this.tradListTopMsgIdx = this.hdrsForCurrentSubBoard.length - this.tradMsgListNumLines;
}
else
{
continueOn = false;
retObj.selectedMsgOffset = -1;
}
}
if (userSettingsRetObj.needWholeScreenRefresh)
this.WriteMsgListScreenTopHeader();
}

nightfox
committed
else
{
// If a message has been selected, exit out of this input loop
// so we can return from this method - The calling method will
// call the enhanced reader method.
if (retObj.selectedMsgOffset >= 0)

nightfox
committed
continueOn = false;
}
}
}
msgbase.close();

nightfox
committed
return retObj;
}
// For the DigDistMsgReader class: Performs the message listing, given a
// sub-board code. This verison uses a lightbar interface for message
// navigation.
//
// Parameters:

nightfox
committed
// pAllowChgSubBoard: Optional - A boolean to specify whether or not to allow
// changing to another sub-board. Defaults to true.
//
// Return value: An object containing the following properties:
// lastUserInput: The user's last keypress/input
// selectedMsgOffset: The index of the message selected to read,
// if one was selected. If none was selected,
// this will be -1.
function DigDistMsgReader_ListMessages_Lightbar(pAllowChgSubBoard)
{
var retObj = {
lastUserInput: "",
selectedMsgOffset: -1
};
// 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("\x01n" + errorMsg);
console.pause();
return retObj;
}
}
// This method is only supported if the user's terminal supports
// ANSI.
if (!canDoHighASCIIAndANSI()) // Could also be !console.term_supports(USER_ANSI)
{
console.print("\r\n\x01h\x01ySorry, an ANSI terminal is required for this operation.\x01n\x01w\r\n");
console.pause();
return retObj;
}
// Reset this.readAMessage and deniedReadingMessage to false, in case the
// message listing has previously ended with them set to true.
this.readAMessage = false;
this.deniedReadingMessage = false;
this.RecalcMsgListWidthsAndFormatStrs();

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

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

Eric Oulashin
committed
// Mouse: console.print replaced with console.putmsg for mouse click hotspots
Eric Oulashin
committed
//console.print(pHelpLineText);
console.putmsg(pHelpLineText); // console.putmsg() can process @-codes, which we use for mouse click tracking
console.cleartoeol("\x01n");

nightfox
committed
}

nightfox
committed
// Clear the screen and write the header at the top
console.clear("\x01n");

nightfox
committed
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
// If the lightbar message list index & cursor position variables haven't been
// set yet, then set them.
if ((this.lightbarListTopMsgIdx == -1) || (this.lightbarListSelectedMsgIdx == -1) ||
(this.lightbarListCurPos == null))
{
this.SetUpLightbarMsgListVars();
}
// Create a DDLightbarMenu for the message list and list messages
// and let the user choose one
var msgListMenu = this.CreateLightbarMsgListMenu();

nightfox
committed
var msgHeader = null;
var drawMenu = true;

nightfox
committed
var continueOn = true;
while (continueOn)
{
var userChoice = msgListMenu.GetVal(drawMenu);
drawMenu = true;
var lastUserInputUpper = (typeof(msgListMenu.lastUserInput) == "string" ? msgListMenu.lastUserInput.toUpperCase() : msgListMenu.lastUserInput);
// If the user's last input is null, then something bad/weird must have
// happened, so don't continue the input loop.
if (lastUserInputUpper == null)
{
continueOn = false;
break;
}
this.lightbarListSelectedMsgIdx = msgListMenu.selectedItemIdx;
// If userChoice is a number, then it will be a message number for a message to read
if (typeof(userChoice) == "number")

nightfox
committed
{
// The user choice a message to read
this.lightbarListSelectedMsgIdx = msgListMenu.selectedItemIdx;
msgHeader = this.GetMsgHdrByIdx(this.lightbarListSelectedMsgIdx, this.showScoresInMsgList);
this.PrintMessageInfo(msgHeader, true, this.lightbarListSelectedMsgIdx+1);
console.gotoxy(this.lightbarListCurPos); // Make sure the cursor is still in the right place
var hdrIsBogus = (msgHeader.hasOwnProperty("isBogus") ? msgHeader.isBogus : false);
if (!hdrIsBogus)

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

nightfox
committed
if (retObj.selectedMsgOffset < 0)
retObj.selectedMsgOffset = 0;
}
// Return from here so that the calling function can switch into
// reader mode.
continueOn = false;
return retObj;
}
else
this.deniedReadingMessage = true;
// Ask the user if they want to continue reading messages
if (this.promptToContinueListingMessages)
continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] + "Continue listing messages");
// If the user chose to continue reading messages, then refresh
// the screen. Even if the user chooses not to read the message,
// the screen needs to be re-drawn so it appears properly.
if (continueOn)
{
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
}

nightfox
committed
}
// If userChoice is not a number, then it should be null in this case,
// and the user would have pressed one of the additional quit keys set
// up for the menu. So look at the menu's lastUserInput and do the
// appropriate thing.
else if ((lastUserInputUpper == this.msgListKeys.quit) || (lastUserInputUpper == KEY_ESC)) // Quit

nightfox
committed
{
continueOn = false;
retObj.lastUserInput = "Q"; // So the reader will quit out

nightfox
committed
}
// Numeric digit: The start of a number of a message to read
else if (lastUserInputUpper.match(/[0-9]/))

nightfox
committed
{
// Put the user's input back in the input buffer to
// be used for getting the rest of the message number.
console.ungetstr(lastUserInputUpper);

nightfox
committed
// Move the cursor to the bottom of the screen and
// prompt the user for the message number.
console.gotoxy(1, console.screen_rows);
Eric Oulashin
committed
var userInput = this.PromptForMsgNum({ x: 1, y: console.screen_rows }, replaceAtCodesInStr(this.text.readMsgNumPromptText), true, ERROR_PAUSE_WAIT_MS, false);

nightfox
committed
if (userInput > 0)
{
// See if the current message header has our "isBogus" property and it's true.
// Only let the user read the message if it's not a bogus message header.
// The message header could have the "isBogus" property, for instance, if
// it's a vote message (introduced in Synchronet 3.17).
//GetMsgHdrByIdx(pMsgIdx, pExpandFields)
var tmpMsgHdr = this.GetMsgHdrByIdx(+(userInput-1), false);
var hdrIsBogus = (tmpMsgHdr.hasOwnProperty("isBogus") ? tmpMsgHdr.isBogus : false);
if (!hdrIsBogus)

nightfox
committed
{
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
var sReadMsgConfirmText = this.colors.readMsgConfirmColor
+ "Read message "
+ this.colors.readMsgConfirmNumberColor
+ userInput + this.colors.readMsgConfirmColor
+ ": Are you sure";
readMsg = console.yesno(sReadMsgConfirmText);
}
if (readMsg)
{
// Update the message list screen variables
this.CalcMsgListScreenIdxVarsFromMsgNum(+userInput);
retObj.selectedMsgOffset = userInput - 1;
// Return from here so that the calling function can switch
// into reader mode.
return retObj;
}
else
this.deniedReadingMessage = true;
// Prompt the user whether or not to continue listing
// messages.
if (this.promptToContinueListingMessages)
continueOn = console.yesno(this.colors.afterReadMsg_ListMorePromptColor + "Continue listing messages");

nightfox
committed
}
else
writeWithPause(1, console.screen_rows, "\x01n\x01h\x01yThat's not a readable message.", ERROR_PAUSE_WAIT_MS, "\x01n", true);

nightfox
committed
}

nightfox
committed
// If the user chose to continue listing messages, then re-draw
// the screen.
if (continueOn)
{
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
}
// DEL key: Delete a message

Eric Oulashin
committed
else if (lastUserInputUpper == this.msgListKeys.deleteMessage || lastUserInputUpper == '\x7f' || lastUserInputUpper == '\x08')

nightfox
committed
{
if (this.CanDelete() || this.CanDeleteLastMsg())
{
console.gotoxy(1, console.screen_rows);
console.print("\x01n");

nightfox
committed
console.clearline();
// The PromptAndDeleteOrUndeleteMessage() method will prompt the user for confirmation
// to delete the message and then delete it if confirmed.
this.PromptAndDeleteOrUndeleteMessage(this.lightbarListSelectedMsgIdx, { x: 1, y: console.screen_rows}, true);
// In case all messages were deleted, if the user can't view deleted messages,
// show an appropriate message and don't continue listing messages.
//if (this.NumMessages(true) == 0)
if (!this.NonDeletedMessagesExist() && !canViewDeletedMsgs())
continueOn = false;
else
{
// There are still some messages to show, so refresh the screen.
// Refresh the header & help line.
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}

nightfox
committed
}
}
// E: Edit a message
else if (lastUserInputUpper == this.msgListKeys.editMsg)

nightfox
committed
{
if (this.CanEdit())
{
// See if the current message header has our "isBogus" property and it's true.
// Only let the user edit the message if it's not a bogus message header.
// The message header could have the "isBogus" property, for instance, if
// it's a vote message (introduced in Synchronet 3.17).
var tmpMsgHdr = this.GetMsgHdrByIdx(this.lightbarListSelectedMsgIdx, false);
var hdrIsBogus = (tmpMsgHdr.hasOwnProperty("isBogus") ? tmpMsgHdr.isBogus : false);
if (!hdrIsBogus)
{
// Ask the user if they really want to edit the message
console.gotoxy(1, console.screen_rows);
console.print("\x01n");
console.clearline();
// Let the user edit the message
//var returnObj = this.EditExistingMsg(tmpMsgHdr.offset);
var returnObj = this.EditExistingMsg(this.lightbarListSelectedMsgIdx);
// Refresh the header & help line
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}

nightfox
committed
}
else
drawMenu = false; // No need to re-draw the menu

nightfox
committed
}
// G: Go to a specific message by # (highlight or place that message on the top)
else if (lastUserInputUpper == this.msgListKeys.goToMsg)

nightfox
committed
{
// Move the cursor to the bottom of the screen and
// prompt the user for a message number.
console.gotoxy(1, console.screen_rows);
Eric Oulashin
committed
var userMsgNum = this.PromptForMsgNum({ x: 1, y: console.screen_rows }, "\n" + replaceAtCodesInStr(this.text.goToMsgNumPromptText), true, ERROR_PAUSE_WAIT_MS, false);
if (userMsgNum > 0)

nightfox
committed
{
// Make sure the message number is for a valid message (i.e., it
// could be an invalid message number if there is a search, where
// not all message numbers are consecutive).
if (this.GetMsgHdrByMsgNum(userMsgNum) != null)
{
// If the message is on the current page, then just go to and
// highlight it. Otherwise, set the user's selected message on the
// top of the page. We also have to make sure that this.lightbarListCurPos.y and
// originalCurpos.y are set correctly. Also, account for search
// results if there are any (we'll need to have the correct array
// index for the search results).
var chosenMsgIndex = userMsgNum - 1;

Eric Oulashin
committed
msgListMenu.selectedItemIdx = chosenMsgIndex;
if ((chosenMsgIndex < msgListMenu.NumItems()) && (chosenMsgIndex >= this.lightbarListTopMsgIdx))
{
this.lightbarListSelectedMsgIdx = chosenMsgIndex;
msgListMenu.selectedItemIdx = this.lightbarListSelectedMsgIdx;
}
else
{
this.lightbarListTopMsgIdx = this.lightbarListSelectedMsgIdx = chosenMsgIndex;
msgListMenu.topItemIdx = this.lightbarListTopMsgIdx;
}
}
else
{
// The user entered an invalid message number
console.print("\x01n" + replaceAtCodesInStr(format(this.text.invalidMsgNumText, userMsgNum)) + "\x01n");
console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
}

nightfox
committed
}
// Refresh the header & help lines

nightfox
committed
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
// C: Change to another message area (sub-board)
else if (lastUserInputUpper == this.msgListKeys.chgMsgArea)

nightfox
committed
{
if (allowChgSubBoard && (this.subBoardCode != "mail"))
{
// Store the current sub-board code so we can see if it changed
var oldSubCode = bbs.cursub_code;
// Let the user choose another message area. If they chose
// a different message area, then set up the message base
// object accordingly.
this.SelectMsgArea();
if (bbs.cursub_code != oldSubCode)
{
var chgSubRetval = this.ChangeSubBoard(bbs.cursub_code);
continueOn = chgSubRetval.succeeded;
if (chgSubRetval.succeeded)
{
console.print("\x01n");
console.gotoxy(1, console.screen_rows);
console.cleartoeol("\x01n");
console.gotoxy(1, console.screen_rows);
console.print("Loading...");
this.PopulateHdrsForCurrentSubBoard();
this.SetUpLightbarMsgListVars();
}

nightfox
committed
}
// Update the lightbar list variables and refresh the header & help lines

nightfox
committed
if (continueOn)
{
console.clear("\x01n");
// Adjust the menu indexes to ensure they're correct for the current sub-board
this.AdjustLightbarMsgListMenuIdxes(msgListMenu);

nightfox
committed
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
}
else
drawMenu = false; // No need to re-draw the menu
}
else if (lastUserInputUpper == this.msgListKeys.showHelp) // Show help
{
console.clear("\x01n");
this.DisplayMsgListHelp(allowChgSubBoard, true);
// Re-draw the message list header & help line before
// the menu is re-drawn
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);

nightfox
committed
}
// Spacebar: Select a message for batch operations (such as batch
// delete, etc.)
else if (lastUserInputUpper == " ")
{
this.ToggleSelectedMessage(this.subBoardCode, this.lightbarListSelectedMsgIdx);
// Have the menu draw only the check character column in the
// next iteration
msgListMenu.nextDrawOnlyItemSubstr = { start: this.MSGNUM_LEN, end: this.MSGNUM_LEN+1 };
}
// Ctrl-A: Select/de-select all messages
else if (lastUserInputUpper == CTRL_A)
{
console.gotoxy(1, console.screen_rows);
console.print("\x01n");
console.clearline();
console.gotoxy(1, console.screen_rows);
// Prompt the user to select All, None (un-select all), or Cancel
console.print("\x01n\x01gSelect \x01c(\x01hA\x01n\x01c)\x01gll, \x01c(\x01hN\x01n\x01c)\x01gone, or \x01c(\x01hC\x01n\x01c)\x01gancel: \x01h\x01g");
var userChoice = getAllowedKeyWithMode("ANC", K_UPPER | K_NOCRLF);
if ((userChoice == "A") || (userChoice == "N"))
{
// Toggle all the messages
var messageSelectToggle = (userChoice == "A");
var totalNumMessages = this.NumMessages();
var messageIndex = 0;
for (messageIndex = 0; messageIndex < totalNumMessages; ++messageIndex)
this.ToggleSelectedMessage(this.subBoardCode, messageIndex, messageSelectToggle);
// Have the menu draw only the check character column in the
// next iteration
msgListMenu.nextDrawOnlyItemSubstr = { start: this.MSGNUM_LEN, end: this.MSGNUM_LEN+1 };
}
else
drawMenu = false; // No need to re-draw the menu
// Refresh the help line
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
// Ctrl-D: Batch delete (for selected messages)
else if (lastUserInputUpper == CTRL_D)
{
if (this.CanDelete() || this.CanDeleteLastMsg())
{
if (this.NumSelectedMessages() > 0)
{
console.gotoxy(1, console.screen_rows);
console.print("\x01n");
console.clearline();
// The PromptAndDeleteOrUndeleteSelectedMessages() method will prompt the user for confirmation
// to delete the message and then delete it if confirmed.
this.PromptAndDeleteOrUndeleteSelectedMessages({ x: 1, y: console.screen_rows}, true);
// In case all messages were deleted, if the user can't view deleted messages,
// show an appropriate message and don't continue listing messages.
//if (this.NumMessages(true) == 0)
if (!this.NonDeletedMessagesExist() && !canViewDeletedMsgs())
continueOn = false;
else
{
// There are still messages to list, so refresh the header & help lines
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
}
else
{
// There are no selected messages

Eric Oulashin
committed
writeWithPause(1, console.screen_rows, "\x01n\x01h\x01yThere are no selected messages.", ERROR_PAUSE_WAIT_MS, "\x01n", true);
// Refresh the help line
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
}
}
// U: Undelete message(s)
else if (lastUserInputUpper == this.msgListKeys.undeleteMessage)
{
if (this.CanDelete() || this.CanDeleteLastMsg())
{
console.gotoxy(1, console.screen_rows);
console.print("\x01n");
console.clearline();
if (this.NumSelectedMessages() > 0)
{
// Multi-message undelete
this.PromptAndDeleteOrUndeleteSelectedMessages({ x: 1, y: console.screen_rows}, false);
}
else
{
// Single-message undelete
this.PromptAndDeleteOrUndeleteMessage(this.lightbarListSelectedMsgIdx, { x: 1, y: console.screen_rows}, false);
}
// Refresh the header & help line.
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
}

Eric Oulashin
committed
else if (lastUserInputUpper == this.msgListKeys.userSettings)
{

Eric Oulashin
committed
var userSettingsRetObj = this.DoUserSettings_Scrollable(function(pReader) { DisplayHelpLine(pReader.msgListLightbarModeHelpLine); });

Eric Oulashin
committed
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
lastUserInputUpper = "";
drawMenu = userSettingsRetObj.needWholeScreenRefresh;
// In case the user changed their twitlist, re-filter the messages for this sub-board
if (userSettingsRetObj.userTwitListChanged)
{
console.gotoxy(1, console.screen_rows);
console.crlf();
console.print("\x01nTwitlist changed; re-filtering..");
var tmpMsgbase = new MsgBase(this.subBoardCode);
if (tmpMsgbase.open())
{
var tmpAllMsgHdrs = tmpMsgbase.get_all_msg_headers(true);
tmpMsgbase.close();
this.FilterMsgHdrsIntoHdrsForCurrentSubBoard(tmpAllMsgHdrs, true);
}
else
console.print("\x01y\x01hFailed to open the messagbase!\x01\r\n\x01p");
this.SetUpLightbarMsgListVars();
msgListMenu = this.CreateLightbarMsgListMenu();
drawMenu = true;
}
if (userSettingsRetObj.needWholeScreenRefresh)
{
this.WriteMsgListScreenTopHeader();
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}
else
msgListMenu.DrawPartialAbs(userSettingsRetObj.optionBoxTopLeftX, userSettingsRetObj.optionBoxTopLeftY, userSettingsRetObj.optionBoxWidth, userSettingsRetObj.optionBoxHeight);
}
// TODO
else if (lastUserInputUpper == "S")
{
// Refresh the help line
DisplayHelpLine(this.msgListLightbarModeHelpLine);
}

nightfox
committed
}
this.lightbarListSelectedMsgIdx = msgListMenu.selectedItemIdx;
this.lightbarListTopMsgIdx = msgListMenu.topItemIdx;

nightfox
committed
return retObj;
}
// For the DigDistMsgLister class: Creates & returns a DDLightbarMenu for
// performing the lightbar message list.
function DigDistMsgReader_CreateLightbarMsgListMenu()
{
// Start & end indexes for the various items in each message list row
var msgListIdxes = {
msgNumStart: 0,
msgNumEnd: this.MSGNUM_LEN,
selectMarkStart: this.MSGNUM_LEN,
selectMarkEnd: this.MSGNUM_LEN+1,
};
msgListIdxes.fromNameStart = this.MSGNUM_LEN + 1;
msgListIdxes.fromNameEnd = msgListIdxes.fromNameStart + +this.FROM_LEN + 1;
msgListIdxes.toNameStart = msgListIdxes.fromNameEnd;
msgListIdxes.toNameEnd = msgListIdxes.toNameStart + +this.TO_LEN + 1;
msgListIdxes.subjStart = msgListIdxes.toNameEnd;
msgListIdxes.subjEnd = msgListIdxes.subjStart + +this.SUBJ_LEN + 1;

Eric Oulashin
committed
if (this.showScoresInMsgList)
{
msgListIdxes.scoreStart = msgListIdxes.subjEnd;
msgListIdxes.scoreEnd = msgListIdxes.scoreStart + +this.SCORE_LEN + 1;
msgListIdxes.dateStart = msgListIdxes.scoreEnd;
}
else
msgListIdxes.dateStart = msgListIdxes.subjEnd;
msgListIdxes.dateEnd = msgListIdxes.dateStart + +this.DATE_LEN + 1;
msgListIdxes.timeStart = msgListIdxes.dateEnd;
msgListIdxes.timeEnd = console.screen_columns - 1; // msgListIdxes.timeStart + +this.TIME_LEN + 1;
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
var msgListMenuHeight = console.screen_rows - this.lightbarMsgListStartScreenRow;
var msgListMenu = new DDLightbarMenu(1, this.lightbarMsgListStartScreenRow, console.screen_columns, msgListMenuHeight);
msgListMenu.scrollbarEnabled = true;
msgListMenu.borderEnabled = false;
msgListMenu.SetColors({
itemColor: [{start: msgListIdxes.msgNumStart, end: msgListIdxes.msgNumEnd, attrs: this.colors.msgListMsgNumColor},
{start: msgListIdxes.selectMarkStart, end: msgListIdxes.selectMarkEnd, attrs: this.colors.selectedMsgMarkColor},
{start: msgListIdxes.fromNameStart, end: msgListIdxes.fromNameEnd, attrs: this.colors.msgListFromColor},
{start: msgListIdxes.toNameStart, end: msgListIdxes.toNameEnd, attrs: this.colors.msgListToColor},
{start: msgListIdxes.subjStart, end: msgListIdxes.subjEnd, attrs: this.colors.msgListSubjectColor},
{start: msgListIdxes.dateStart, end: msgListIdxes.dateEnd, attrs: this.colors.msgListDateColor},
{start: msgListIdxes.timeStart, end: msgListIdxes.timeEnd, attrs: this.colors.msgListTimeColor}],
altItemColor: [{start: msgListIdxes.msgNumStart, end: msgListIdxes.msgNumEnd, attrs: this.colors.msgListToUserMsgNumColor},
{start: msgListIdxes.selectMarkStart, end: msgListIdxes.selectMarkEnd, attrs: this.colors.selectedMsgMarkColor},
{start: msgListIdxes.fromNameStart, end: msgListIdxes.fromNameEnd, attrs: this.colors.msgListToUserFromColor},
{start: msgListIdxes.toNameStart, end: msgListIdxes.toNameEnd, attrs: this.colors.msgListToUserToColor},
{start: msgListIdxes.subjStart, end: msgListIdxes.subjEnd, attrs: this.colors.msgListToUserSubjectColor},
{start: msgListIdxes.dateStart, end: msgListIdxes.dateEnd, attrs: this.colors.msgListToUserDateColor},
{start: msgListIdxes.timeStart, end: msgListIdxes.timeEnd, attrs: this.colors.msgListToUserTimeColor}],
selectedItemColor: [{start: msgListIdxes.msgNumStart, end: msgListIdxes.msgNumEnd, attrs: this.colors.msgListMsgNumHighlightColor},
{start: msgListIdxes.selectMarkStart, end: msgListIdxes.selectMarkEnd, attrs: this.colors.selectedMsgMarkColor + this.colors.msgListHighlightBkgColor},
{start: msgListIdxes.fromNameStart, end: msgListIdxes.fromNameEnd, attrs: this.colors.msgListFromHighlightColor},
{start: msgListIdxes.toNameStart, end: msgListIdxes.toNameEnd, attrs: this.colors.msgListToHighlightColor},
{start: msgListIdxes.subjStart, end: msgListIdxes.subjEnd, attrs: this.colors.msgListSubjHighlightColor},
{start: msgListIdxes.dateStart, end: msgListIdxes.dateEnd, attrs: this.colors.msgListDateHighlightColor},
{start: msgListIdxes.timeStart, end: msgListIdxes.timeEnd, attrs: this.colors.msgListTimeHighlightColor}],
altSelectedItemColor: [{start: msgListIdxes.msgNumStart, end: msgListIdxes.msgNumEnd, attrs: this.colors.msgListMsgNumHighlightColor},
{start: msgListIdxes.selectMarkStart, end: msgListIdxes.selectMarkEnd, attrs: this.colors.selectedMsgMarkColor + this.colors.msgListHighlightBkgColor},
{start: msgListIdxes.fromNameStart, end: msgListIdxes.fromNameEnd, attrs: this.colors.msgListFromHighlightColor},
{start: msgListIdxes.toNameStart, end: msgListIdxes.toNameEnd, attrs: this.colors.msgListToHighlightColor},
{start: msgListIdxes.subjStart, end: msgListIdxes.subjEnd, attrs: this.colors.msgListSubjHighlightColor},
{start: msgListIdxes.dateStart, end: msgListIdxes.dateEnd, attrs: this.colors.msgListDateHighlightColor},
{start: msgListIdxes.timeStart, end: msgListIdxes.timeEnd, attrs: this.colors.msgListTimeHighlightColor}]
});

Eric Oulashin
committed
// If we are to show message vote scores in the list (i.e., if the
// user's terminal is wide enough), then splice in color specifiers
// for the score column.
if (this.showScoresInMsgList)
{
msgListMenu.colors.itemColor.splice(5, 0, {start: msgListIdxes.scoreStart, end: msgListIdxes.scoreEnd, attrs: this.colors.msgListScoreColor});
msgListMenu.colors.altItemColor.splice(5, 0, {start: msgListIdxes.scoreStart, end: msgListIdxes.scoreEnd, attrs: this.colors.msgListToUserScoreColor});
msgListMenu.colors.selectedItemColor.splice(5, 0, {start: msgListIdxes.scoreStart, end: msgListIdxes.scoreEnd, attrs: this.colors.msgListScoreHighlightColor + this.colors.msgListHighlightBkgColor});
msgListMenu.colors.altSelectedItemColor.splice(5, 0, {start: msgListIdxes.scoreStart, end: msgListIdxes.scoreEnd, attrs: this.colors.msgListScoreHighlightColor + this.colors.msgListHighlightBkgColor});
}
msgListMenu.multiSelect = false;
msgListMenu.ampersandHotkeysInItems = false;
msgListMenu.wrapNavigation = false;
// Add additional keypresses for quitting the menu's input loop so we can
// respond to these keys

Eric Oulashin
committed
// Ctrl-A: Select all messages
var additionalQuitKeys = "EeqQgGcCsS ?0123456789" + CTRL_A + this.msgListKeys.batchDelete + this.msgListKeys.userSettings;
if (this.CanDelete() || this.CanDeleteLastMsg())

Eric Oulashin
committed
{

Eric Oulashin
committed
additionalQuitKeys += this.msgListKeys.deleteMessage + this.msgListKeys.undeleteMessage.toLowerCase() + this.msgListKeys.undeleteMessage.toUpperCase();

Eric Oulashin
committed
additionalQuitKeys += '\x7f'; // Ensure DEL is in there
additionalQuitKeys += '\x08'; // Ensure BACKSPACE is in there (can be an alternate for delete)
}
if (this.CanEdit())

Eric Oulashin
committed
additionalQuitKeys += this.msgListKeys.editMsg;
msgListMenu.AddAdditionalQuitKeys(additionalQuitKeys);
// Change the menu's NumItems() and GetItem() function to reference
// the message list in this object rather than add the menu items
// to the menu
msgListMenu.msgReader = this; // Add this object to the menu object
msgListMenu.NumItems = function() {
return this.msgReader.NumMessages();
};
msgListMenu.GetItem = function(pItemIndex) {
var menuItemObj = this.MakeItemWithRetval(-1);
var itemIdx = (this.msgReader.reverseListOrder ? this.msgReader.NumMessages() - pItemIndex - 1 : pItemIndex);

Eric Oulashin
committed
// In order to get vote score information (displayed if the user's terminal is wide
// enough), the 2nd parameter to GetMsgHdrByIdx() should be true.
var msgHdr = this.msgReader.GetMsgHdrByIdx(itemIdx, this.msgReader.showScoresInMsgList);
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
if (msgHdr != null)
{
// When setting the item text, call PrintMessageInfo with true as
// the last parameter to return the string instead
menuItemObj.text = strip_ctrl(this.msgReader.PrintMessageInfo(msgHdr, false, itemIdx+1, true));
menuItemObj.retval = msgHdr.number;
if (this.msgReader.subBoardCode != "mail")
menuItemObj.useAltColors = userHandleAliasNameMatch(msgHdr.to);
// If the message is marked as deleted, ensure the correct color is used
// for the mark character in the menu
if ((msgHdr.attr & MSG_DELETE) == MSG_DELETE)
{
var fromColor = this.msgReader.colors.msgListFromColor;
var toColor = this.msgReader.colors.msgListToColor;
var subjColor = this.msgReader.colors.msgListSubjectColor;
if ((this.msgReader.subBoardCode != "mail") && (userHandleAliasNameMatch(msgHdr.to)))
{
fromColor = this.msgReader.colors.msgListToUserFromColor;
toColor = this.msgReader.colors.msgListToUserToColor;
subjColor = this.msgReader.colors.msgListToUserSubjectColor;
}
menuItemObj.itemColor = [{start: msgListIdxes.msgNumStart, end: msgListIdxes.msgNumEnd, attrs: this.msgReader.colors.msgListMsgNumColor},
{start: msgListIdxes.selectMarkStart, end: msgListIdxes.selectMarkEnd, attrs: "\x01r\x01h\x01i"},
{start: msgListIdxes.fromNameStart, end: msgListIdxes.fromNameEnd, attrs: fromColor},
{start: msgListIdxes.toNameStart, end: msgListIdxes.toNameEnd, attrs: toColor},
{start: msgListIdxes.subjStart, end: msgListIdxes.subjEnd, attrs: subjColor},
{start: msgListIdxes.dateStart, end: msgListIdxes.dateEnd, attrs: this.msgReader.colors.msgListDateColor},
{start: msgListIdxes.timeStart, end: msgListIdxes.timeEnd, attrs: this.msgReader.colors.msgListTimeColor}];
menuItemObj.itemSelectedColor = [{start: msgListIdxes.msgNumStart, end: msgListIdxes.msgNumEnd, attrs: this.msgReader.colors.msgListMsgNumHighlightColor},
{start: msgListIdxes.selectMarkStart, end: msgListIdxes.selectMarkEnd, attrs: "\x01r\x01h\x01i" + this.msgReader.colors.msgListHighlightBkgColor},
{start: msgListIdxes.fromNameStart, end: msgListIdxes.fromNameEnd, attrs: this.msgReader.colors.msgListFromHighlightColor},
{start: msgListIdxes.toNameStart, end: msgListIdxes.toNameEnd, attrs: this.msgReader.colors.msgListToHighlightColor},
{start: msgListIdxes.subjStart, end: msgListIdxes.subjEnd, attrs: this.msgReader.colors.msgListSubjHighlightColor},
{start: msgListIdxes.dateStart, end: msgListIdxes.dateEnd, attrs: this.msgReader.colors.msgListDateHighlightColor},
{start: msgListIdxes.timeStart, end: msgListIdxes.timeEnd, attrs: this.msgReader.colors.msgListTimeHighlightColor}];
}
}
return menuItemObj;
};
// Adjust the menu indexes to ensure they're correct for the current sub-board
this.AdjustLightbarMsgListMenuIdxes(msgListMenu);
return msgListMenu;
}
// For the DigDistMsgLister class: Creates a DDLightbarMenu object for the user to choose
// a message group.
//
// Return value: A DDLightbarMenu object set up to let the user choose a message group
function DigDistMsgReader_CreateLightbarMsgGrpMenu()
{
// Start & end indexes for the various items in each mssage group list row
// Selected mark, group#, description, # sub-boards
var msgGrpListIdxes = {
markCharStart: 0,
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
markCharEnd: 1,
grpNumStart: 1,
grpNumEnd: 2 + (+this.areaNumLen)
};
msgGrpListIdxes.descStart = msgGrpListIdxes.grpNumEnd;
msgGrpListIdxes.descEnd = msgGrpListIdxes.descStart + +this.msgGrpDescLen;
msgGrpListIdxes.numItemsStart = msgGrpListIdxes.descEnd;
msgGrpListIdxes.numItemsEnd = msgGrpListIdxes.numItemsStart + +this.numItemsLen;
// Set numItemsEnd to -1 to let the whole rest of the lines be colored
msgGrpListIdxes.numItemsEnd = -1;
var listStartRow = this.areaChangeHdrLines.length + 2;
var msgGrpMenuHeight = console.screen_rows - listStartRow;
var msgGrpMenu = new DDLightbarMenu(1, listStartRow, console.screen_columns, msgGrpMenuHeight);
msgGrpMenu.scrollbarEnabled = true;
msgGrpMenu.borderEnabled = false;
msgGrpMenu.SetColors({
itemColor: [{start: msgGrpListIdxes.markCharStart, end: msgGrpListIdxes.markCharEnd, attrs: this.colors.areaChooserMsgAreaMarkColor},
{start: msgGrpListIdxes.grpNumStart, end: msgGrpListIdxes.grpNumEnd, attrs: this.colors.areaChooserMsgAreaNumColor},
{start: msgGrpListIdxes.descStart, end: msgGrpListIdxes.descEnd, attrs: this.colors.areaChooserMsgAreaDescColor},
{start: msgGrpListIdxes.numItemsStart, end: msgGrpListIdxes.numItemsEnd, attrs: this.colors.areaChooserMsgAreaNumItemsColor}],
selectedItemColor: [{start: msgGrpListIdxes.markCharStart, end: msgGrpListIdxes.markCharEnd, attrs: this.colors.areaChooserMsgAreaMarkColor + this.colors.areaChooserMsgAreaBkgHighlightColor},
{start: msgGrpListIdxes.grpNumStart, end: msgGrpListIdxes.grpNumEnd, attrs: this.colors.areaChooserMsgAreaNumHighlightColor},
{start: msgGrpListIdxes.descStart, end: msgGrpListIdxes.descEnd, attrs: this.colors.areaChooserMsgAreaDescHighlightColor},
{start: msgGrpListIdxes.numItemsStart, end: msgGrpListIdxes.numItemsEnd, attrs: this.colors.areaChooserMsgAreaNumItemsHighlightColor}]
});
msgGrpMenu.multiSelect = false;
msgGrpMenu.ampersandHotkeysInItems = false;
msgGrpMenu.wrapNavigation = false;
// Add additional keypresses for quitting the menu's input loop so we can
// respond to these keys
msgGrpMenu.AddAdditionalQuitKeys("nNqQ ?0123456789/" + CTRL_F);
// Change the menu's NumItems() and GetItem() function to reference
// the message list in this object rather than add the menu items
// to the menu
msgGrpMenu.msgReader = this; // Add this object to the menu object
msgGrpMenu.NumItems = function() {
return msg_area.grp_list.length;
};
msgGrpMenu.GetItem = function(pGrpIndex) {
var menuItemObj = this.MakeItemWithRetval(-1);
if ((pGrpIndex >= 0) && (pGrpIndex < msg_area.grp_list.length))
{
menuItemObj.text = format(((typeof(bbs.curgrp) == "number") && (pGrpIndex == msg_area.sub[this.msgReader.subBoardCode].grp_index)) ? "*" : " ");
menuItemObj.text += format(this.msgReader.msgGrpListPrintfStr, +(pGrpIndex+1),
msg_area.grp_list[pGrpIndex].description.substr(0, this.msgReader.msgGrpDescLen),
msg_area.grp_list[pGrpIndex].sub_list.length);
menuItemObj.text = strip_ctrl(menuItemObj.text);
menuItemObj.retval = pGrpIndex;
}
return menuItemObj;
};
// Set the currently selected item to the current group
msgGrpMenu.selectedItemIdx = msg_area.sub[this.subBoardCode].grp_index;
if (msgGrpMenu.selectedItemIdx >= msgGrpMenu.topItemIdx+msgGrpMenu.GetNumItemsPerPage())
msgGrpMenu.topItemIdx = msgGrpMenu.selectedItemIdx - msgGrpMenu.GetNumItemsPerPage() + 1;
return msgGrpMenu;
}
// For the DigDistMsgLister class: Creates a DDLightbarMenu object for the user to choose
// a sub-board within a message group.
//
// Parameters:
// pGrpIdx: The index of the group to list sub-boards for
//
// Return value: A DDLightbarMenu object set up to let the user choose a sub-board within the
// given message group
function DigDistMsgReader_CreateLightbarSubBoardMenu(pGrpIdx)
{
// Start & end indexes for the various items in each sub-board list row
// Selected mark, group#, description, # sub-boards
var subBrdListIdxes = {
markCharStart: 0,
markCharEnd: 1,
subNumStart: 1,
subNumEnd: 2 + (+this.areaNumLen)
};
subBrdListIdxes.descStart = subBrdListIdxes.subNumEnd;
subBrdListIdxes.descEnd = subBrdListIdxes.descStart + +(this.subBoardListPrintfInfo[pGrpIdx].nameLen) + 1;
subBrdListIdxes.numItemsStart = subBrdListIdxes.descEnd;
subBrdListIdxes.numItemsEnd = subBrdListIdxes.numItemsStart + +(this.subBoardListPrintfInfo[pGrpIdx].numMsgsLen) + 1;
subBrdListIdxes.dateStart = subBrdListIdxes.numItemsEnd;
subBrdListIdxes.dateEnd = subBrdListIdxes.dateStart + +this.dateLen + 1;
subBrdListIdxes.timeStart = subBrdListIdxes.dateEnd;
// Set timeEnd to -1 to let the whole rest of the lines be colored
subBrdListIdxes.timeEnd = -1;
var listStartRow = this.areaChangeHdrLines.length + 3;
var subBrdMenuHeight = console.screen_rows - listStartRow;
var subBoardMenu = new DDLightbarMenu(1, listStartRow, console.screen_columns, subBrdMenuHeight);
subBoardMenu.scrollbarEnabled = true;
subBoardMenu.borderEnabled = false;
subBoardMenu.SetColors({
itemColor: [{start: subBrdListIdxes.markCharStart, end: subBrdListIdxes.markCharEnd, attrs: this.colors.areaChooserMsgAreaMarkColor},
{start: subBrdListIdxes.subNumStart, end: subBrdListIdxes.subNumEnd, attrs: this.colors.areaChooserMsgAreaNumColor},
{start: subBrdListIdxes.descStart, end: subBrdListIdxes.descEnd, attrs: this.colors.areaChooserMsgAreaDescColor},
{start: subBrdListIdxes.numItemsStart, end: subBrdListIdxes.numItemsEnd, attrs: this.colors.areaChooserMsgAreaNumItemsColor},
{start: subBrdListIdxes.dateStart, end: subBrdListIdxes.dateEnd, attrs: this.colors.areaChooserMsgAreaLatestDateColor},
{start: subBrdListIdxes.timeStart, end: subBrdListIdxes.timeEnd, attrs: this.colors.areaChooserMsgAreaLatestTimeColor}],
selectedItemColor: [{start: subBrdListIdxes.markCharStart, end: subBrdListIdxes.markCharEnd, attrs: this.colors.areaChooserMsgAreaMarkColor + this.colors.areaChooserMsgAreaBkgHighlightColor},
{start: subBrdListIdxes.subNumStart, end: subBrdListIdxes.subNumEnd, attrs: this.colors.areaChooserMsgAreaNumHighlightColor},
{start: subBrdListIdxes.descStart, end: subBrdListIdxes.descEnd, attrs: this.colors.areaChooserMsgAreaDescHighlightColor},
{start: subBrdListIdxes.numItemsStart, end: subBrdListIdxes.numItemsEnd, attrs: this.colors.areaChooserMsgAreaNumItemsHighlightColor},
{start: subBrdListIdxes.dateStart, end: subBrdListIdxes.dateEnd, attrs: this.colors.areaChooserMsgAreaDateHighlightColor},
{start: subBrdListIdxes.timeStart, end: subBrdListIdxes.timeEnd, attrs: this.colors.areaChooserMsgAreaTimeHighlightColor}]
});
subBoardMenu.multiSelect = false;
subBoardMenu.ampersandHotkeysInItems = false;
subBoardMenu.wrapNavigation = false;
// Add additional keypresses for quitting the menu's input loop so we can
// respond to these keys
subBoardMenu.AddAdditionalQuitKeys("nNqQ ?0123456789/" + CTRL_F);
// Change the menu's NumItems() and GetItem() function to reference
// the message list in this object rather than add the menu items
// to the menu
subBoardMenu.msgReader = this; // Add this object to the menu object
subBoardMenu.grpIdx = pGrpIdx;
subBoardMenu.NumItems = function() {
return msg_area.grp_list[pGrpIdx].sub_list.length;
};
subBoardMenu.GetItem = function(pSubIdx) {
var menuItemObj = this.MakeItemWithRetval(-1);
if ((pSubIdx >= 0) && (pSubIdx < msg_area.grp_list[this.grpIdx].sub_list.length))
{
//var highlight = (msg_area.grp_list[this.grpIdx].sub_list[pSubIdx].code.toUpperCase() == this.msgReader.subBoardCode.toUpperCase());
menuItemObj.text = this.msgReader.GetMsgSubBoardLine(this.grpIdx, pSubIdx, false);
menuItemObj.text = strip_ctrl(menuItemObj.text);
menuItemObj.retval = pSubIdx;
}
return menuItemObj;
};
// Set the currently selected item to the current group
if (msg_area.sub[this.subBoardCode].grp_index == pGrpIdx)
{
subBoardMenu.selectedItemIdx = msg_area.sub[this.subBoardCode].index;
if (subBoardMenu.selectedItemIdx >= subBoardMenu.topItemIdx+subBoardMenu.GetNumItemsPerPage())
subBoardMenu.topItemIdx = subBoardMenu.selectedItemIdx - subBoardMenu.GetNumItemsPerPage() + 1;
}
else
{
subBoardMenu.selectedItemIdx = 0;
subBoardMenu.topItemIdx = 0;
}
return subBoardMenu;
}
// For the DigDistMsgLister class: Adjusts lightbar menu indexes for a message list menu
function DigDistMsgReader_AdjustLightbarMsgListMenuIdxes(pMsgListMenu)
{
pMsgListMenu.selectedItemIdx = this.lightbarListSelectedMsgIdx;
pMsgListMenu.topItemIdx = this.lightbarListTopMsgIdx;
// In the DDLightbarMenu class, the top index on the last page should
// allow for displaying a full page of items. So if
// this.lightbarListTopMsgIdx is beyond the top index for the last
// page in the menu object, then adjust this.lightbarListTopMsgIdx.
var menuTopItemIdxOnLastPage = pMsgListMenu.GetTopItemIdxOfLastPage();
if (pMsgListMenu.topItemIdx > menuTopItemIdxOnLastPage)
{
pMsgListMenu.topItemIdx = menuTopItemIdxOnLastPage;
this.lightbarListTopMsgIdx = menuTopItemIdxOnLastPage;
}
// TODO: Ensure this.lightbarListTopMsgIdx is always correct for the last page
}
// For the DigDistMsgListerClass: Prints a line of information about
// a message.
//
// Parameters:
// pMsgHeader: The message header object, returned by MsgBase.get_msg_header().
// pHighlight: Optional boolean - Whether or not to highlight the line (true) or
// use the standard colors (false).
// pMsgNum: Optional - A number to use for the message instead of the number/offset
// in the message header
// pReturnStrInstead: Optional boolean - Whether or not to return a formatted string
// instead of printing to the console. Defaults to false.
function DigDistMsgReader_PrintMessageInfo(pMsgHeader, pHighlight, pMsgNum, pReturnStrInstead)
{
// pMsgHeader must be a valid object.
if (typeof(pMsgHeader) == "undefined")
return;
if (pMsgHeader == null)
return;
var highlight = false;
if (typeof(pHighlight) == "boolean")
highlight = pHighlight;
// Get the message's import date & time as strings. If
// this.msgList_displayMessageDateImported is true, use the message import date.
// Otherwise, use the message written date.
var sDate;
var sTime;
if (this.msgList_displayMessageDateImported)
{
sDate = strftime("%Y-%m-%d", pMsgHeader.when_imported_time);
sTime = strftime("%H:%M:%S", pMsgHeader.when_imported_time);
}
else
{
//sDate = strftime("%Y-%m-%d", pMsgHeader.when_written_time);
//sTime = strftime("%H:%M:%S", pMsgHeader.when_written_time);
var msgWrittenLocalTime = msgWrittenTimeToLocalBBSTime(pMsgHeader);
if (msgWrittenLocalTime != -1)
{
sDate = strftime("%Y-%m-%d", msgWrittenLocalTime);
sTime = strftime("%H:%M:%S", msgWrittenLocalTime);
}
else
{
sDate = strftime("%Y-%m-%d", pMsgHeader.when_written_time);
sTime = strftime("%H:%M:%S", pMsgHeader.when_written_time);
}
}
//var msgNum = (typeof(pMsgNum) == "number" ? pMsgNum : pMsgHeader.offset+1);
var msgNum = (typeof(pMsgNum) == "number" ? pMsgNum : this.GetMsgIdx(pMsgHeader.number)+1);

nightfox
committed
if (msgNum == 0) // In case GetMsgIdx() returns -1 for failure
msgNum = 1;
// Determine if the message has been deleted.
var msgDeleted = ((pMsgHeader.attr & MSG_DELETE) == MSG_DELETE);
// msgIndicatorChar will contain (possibly) a character to display after
// the message number to indicate whether it has been deleted, selected,
// etc. If not, then it will just be a space.
var msgIndicatorChar = " ";
// Get the message score value
var msgVoteInfo = getMsgUpDownvotesAndScore(pMsgHeader);
// Ensure the score number can fit within 4 digits
if (msgVoteInfo.voteScore > 9999)
msgVoteInfo.voteScore = 9999;
else if (msgVoteInfo.voteScore < -999)
msgVoteInfo.voteScore = -999;
// Generate the string with the message header information.
var msgHdrStr = "";
// Note: The message header has the following fields:
// 'number': The message number
// 'offset': The message offset
// 'to': Who the message is directed to (string)
// 'from' Who wrote the message (string)
// 'subject': The message subject (string)
// 'date': The date - Full text (string)
// To access one of these, use brackets; i.e., msgHeader['to']
if (highlight)
{
// For any indicator character next to the message, prioritize selected, then deleted, then attachments
if (this.MessageIsSelected(this.subBoardCode, msgNum-1))
msgIndicatorChar = "\x01n" + this.colors.selectedMsgMarkColor + this.colors.msgListHighlightBkgColor + CHECK_CHAR + "\x01n";
else if (msgDeleted)
msgIndicatorChar = "\x01n\x01r\x01h\x01i" + this.colors.msgListHighlightBkgColor + "*\x01n";
else if (msgHdrHasAttachmentFlag(pMsgHeader))
msgIndicatorChar = "\x01n" + this.colors.selectedMsgMarkColor + this.colors.msgListHighlightBkgColor + "A\x01n";
var fromName = pMsgHeader.from;
// If the message was posted anonymously and the logged-in user is
// not the sysop, then show "Anonymous" for the 'from' name.
Eric Oulashin
committed
if (!user.is_sysop && ((pMsgHeader.attr & MSG_ANONYMOUS) == MSG_ANONYMOUS))
fromName = "Anonymous";
if (this.showScoresInMsgList)
{
msgHdrStr += format(this.sMsgInfoFormatHighlightStr, msgNum, msgIndicatorChar,
fromName.substr(0, this.FROM_LEN),
pMsgHeader.to.substr(0, this.TO_LEN),
pMsgHeader.subject.substr(0, this.SUBJ_LEN),
msgVoteInfo.voteScore, sDate, sTime);
}
else
{
msgHdrStr += format(this.sMsgInfoFormatHighlightStr, msgNum, msgIndicatorChar,
fromName.substr(0, this.FROM_LEN),
pMsgHeader.to.substr(0, this.TO_LEN),
pMsgHeader.subject.substr(0, this.SUBJ_LEN),
sDate, sTime);
}
}
else
{
// For any indicator character next to the message, prioritize selected, then deleted, then attachments
if (this.MessageIsSelected(this.subBoardCode, msgNum-1))
msgIndicatorChar = "\x01n" + this.colors.selectedMsgMarkColor + CHECK_CHAR + "\x01n";
else if (msgDeleted)
msgIndicatorChar = "\x01n\x01r\x01h\x01i*\x01n";
else if (msgHdrHasAttachmentFlag(pMsgHeader))
msgIndicatorChar = "\x01n" + this.colors.selectedMsgMarkColor + "A\x01n";
// Determine whether to use the normal, "to-user", or "from-user" format string.
// The differences are the colors. Then, output the message information line.
var toNameUpper = pMsgHeader.to.toUpperCase();
var msgToUser = ((toNameUpper == user.alias.toUpperCase()) || (toNameUpper == user.name.toUpperCase()) || (toNameUpper == user.handle.toUpperCase()));
var fromNameUpper = pMsgHeader.from.toUpperCase();
var msgIsFromUser = ((fromNameUpper == user.alias.toUpperCase()) || (fromNameUpper == user.name.toUpperCase()) || (fromNameUpper == user.handle.toUpperCase()));
var formatStr = ""; // Format string for printing the message information
if (this.readingPersonalEmail)
formatStr = this.sMsgInfoFormatStr;
else
formatStr = (msgToUser ? this.sMsgInfoToUserFormatStr : (msgIsFromUser ? this.sMsgInfoFromUserFormatStr : this.sMsgInfoFormatStr));
var fromName = pMsgHeader.from;
// If the message was posted anonymously and the logged-in user is
// not the sysop, then show "Anonymous" for the 'from' name.
Eric Oulashin
committed
if (!user.is_sysop && ((pMsgHeader.attr & MSG_ANONYMOUS) == MSG_ANONYMOUS))
fromName = "Anonymous";
if (this.showScoresInMsgList)
{
msgHdrStr += format(formatStr, msgNum, msgIndicatorChar, fromName.substr(0, this.FROM_LEN),
pMsgHeader.to.substr(0, this.TO_LEN), pMsgHeader.subject.substr(0, this.SUBJ_LEN),
msgVoteInfo.voteScore, sDate, sTime);
}
else
{
msgHdrStr += format(formatStr, msgNum, msgIndicatorChar, fromName.substr(0, this.FROM_LEN),
pMsgHeader.to.substr(0, this.TO_LEN), pMsgHeader.subject.substr(0, this.SUBJ_LEN),
sDate, sTime);
}
}
var returnStrInstead = (typeof(pReturnStrInstead) == "boolean" ? pReturnStrInstead : false);
if (!returnStrInstead)
{
console.print(msgHdrStr);
console.cleartoeol("\x01n"); // To clear away any extra text that may have been entered by the user
}
return msgHdrStr;
}
// For the traditional interface of DigDistMsgListerClass: Prompts the user to
// continue or read a message (by number).
//
// Parameters:
// pStart: Whether or not we're on the first page (true or false)
// pEnd: Whether or not we're at the last page (true or false)

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

nightfox
committed
// Create the return object and set some initial default values
var retObj = {
continueOn: true,
userInput: "",
selectedMsgOffset: -1
};

nightfox
committed
var allowChgSubBoard = (typeof(pAllowChgSubBoard) == "boolean" ? pAllowChgSubBoard : true);
var continueOn = true;
// Prompt the user whether or not to continue or to read a message
// (by message number). Make use of the different prompt texts,
// depending whether we're at the beginning, in the middle, or at
// the end of the message list.
var userInput = "";
var allowedKeys = "?GS"; // ? = help, G = Go to message #, S = Select message(s), Ctrl-D: Batch delete

Eric Oulashin
committed
allowedKeys += this.msgListKeys.userSettings;

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

Eric Oulashin
committed
{

nightfox
committed
allowedKeys += "D"; // Delete

Eric Oulashin
committed
allowedKeys += this.enhReaderKeys.deleteMessage;
allowedKeys += '\x7f';
allowedKeys += '\x08';
}

nightfox
committed
if (this.CanEdit())
allowedKeys += "E"; // Edit
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
if (pStart && pEnd)
{
// This is the only page.
console.print(this.msgListOnlyOnePageContinuePrompt);
// Get input from the user. Allow only Q (quit).
allowedKeys += "Q";
}
else if (pStart)
{
// We're on the first page.
console.print(this.sStartContinuePrompt);
// Get input from the user. Allow only L (last), N (next), or Q (quit).
allowedKeys += "LNQ";
}
else if (pEnd)
{
// We're on the last page.
console.print(this.sEndContinuePrompt);
// Get input from the user. Allow only F (first), P (previous), or Q (quit).
allowedKeys += "FPQ";
}
else
{
// We're neither on the first nor last page. Allow F (first), L (last),
// N (next), P (previous), or Q (quit).
console.print(this.sContinuePrompt);
allowedKeys += "FLNPQ";
}
// Get the user's input. Allow CTRL-D (batch delete) without echoing it.
// If the user didn't press CTRL-L, allow the keys in allowedKeys or a number from 1
// to the highest message number.
userInput = console.getkey(K_NOECHO);
if (userInput != CTRL_D)
{
console.ungetstr(userInput);
userInput = console.getkeys(allowedKeys, this.HighestMessageNum()).toString();
}
if (userInput == "Q")
continueOn = false;
// If the user has typed all numbers, then read that message.
if ((userInput != "") && /^[0-9]+$/.test(userInput))
{
// If the user entered a valid message number, then let the user read the message.
// The message number might be invalid if there are search results that
// have non-continuous message numbers.
if (this.IsValidMessageNum(userInput))
{
// See if the current message header has our "isBogus" property and it's true.
// Only let the user read the message if it's not a bogus message header.
// The message header could have the "isBogus" property, for instance, if
// it's a vote message (introduced in Synchronet 3.17).
var tmpMsgHdr = this.GetMsgHdrByIdx(+(userInput-1), false);
var hdrIsBogus = (tmpMsgHdr.hasOwnProperty("isBogus") ? tmpMsgHdr.isBogus : false);
if (!hdrIsBogus)
{
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ userInput + this.colors["readMsgConfirmColor"]
+ ": Are you sure";
readMsg = console.yesno(sReadMsgConfirmText);
}
if (readMsg)
{
// Update the message list screen variables
this.CalcMsgListScreenIdxVarsFromMsgNum(+userInput);
// Return from here so that the calling function can switch
// into reader mode.
retObj.continueOn = continueOn;
retObj.userInput = userInput;
retObj.selectedMsgOffset = userInput-1;
return retObj;
}
else
this.deniedReadingMessage = true;
// Prompt the user whether or not to continue listing
// messages.
if (this.promptToContinueListingMessages)
{
continueOn = console.yesno(this.colors["afterReadMsg_ListMorePromptColor"] +
"Continue listing messages");
}
}
else
{
console.print("\x01n\x01h\x01yThat's not a readable message.\x01n");
console.crlf();
console.pause();
}
}
else
{
// The user entered an invalid message number.
console.print("\x01n\x01h\x01w" + userInput + " \x01y is not a valid message number.\x01n");
console.crlf();
console.pause();
continueOn = true;
}
}
// Make sure color highlighting is turned off
console.print("\x01n");
// Fill the return object with the required values, and return it.
retObj.continueOn = continueOn;
retObj.userInput = userInput;
return retObj;
}
// For the DigDistMsgReader Class: Given a message number of a message in the
// current message area, lets the user read a message and allows the user to
// respond, etc. This is an enhanced version that allows scrolling up & down
// the message with the up & down arrow keys, and the left & right arrow keys
// will return from the function to allow calling code to navigate back & forth
// through the message sub-board.
//
// Parameters:
// pOffset: The offset of the message to be read
// pAllowChgArea: Optional boolean - Whether or not to allow changing the
// message area
//
// Return value: And object with the following properties:
// offsetValid: Boolean - Whether or not the passed-in offset was valid
// msgDeleted: Boolean - Whether or not the message is marked as deleted
// (not deleted by the user in the reader)
// userReplied: Boolean - Whether or not the user replied to the message.
// lastKeypress: The last keypress from the user - For navigation purposes
// newMsgOffset: The offset of another message to read, if the user
// input another message number. If the user did not
// input another message number, this will be -1.
// nextAction: The next action for the caller to take. This will be
// one of the values specified by the ACTION_* constants.
// This defaults to ACTION_NONE on error.
// refreshEnhancedRdrHelpLine: Boolean - Whether or not to refresh the
// enhanced reader help line on the screen
// (for instance, if switched to the traditional
// non-scrolling interface to read the message)
function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea)
{
var retObj = {
offsetValid: true,
msgNotReadable: false,
userReplied: false,
lastKeypress: "",
newMsgOffset: -1,
nextAction: ACTION_NONE,
refreshEnhancedRdrHelpLine: false
};
// Get the message header. Don't expand fields since we may need to save
// the header later with the MSG_READ attribute.
//var msgHeader = this.GetMsgHdrByIdx(pOffset, false);
// Get the message header. Get expanded fields so that we can show any
// voting stats/responses that may be included with the message.
var msgHeader = this.GetMsgHdrByIdx(pOffset, true);
if (msgHeader == null)
{

Eric Oulashin
committed
//console.print("\x01n" + replaceAtCodesInStr(format(this.text.invalidMsgNumText, +(pOffset+1))) + "\x01n");
//console.crlf();
console.putmsg("\x01n" + format(this.text.invalidMsgNumText, +(pOffset+1)) + "\x01n");
console.inkey(K_NONE, ERROR_PAUSE_WAIT_MS);
retObj.offsetValid = false;
return retObj;
}

nightfox
committed
// If this message is not readable for the user (it's marked as deleted and
// the system is set to not show deleted messages, etc.), then don't let the
// user read it, and just silently return.
// TODO: If the message is not readable, this will end up causing an infinite loop.

nightfox
committed
retObj.msgNotReadable = !isReadableMsgHdr(msgHeader, this.subBoardCode);
if (retObj.msgNotReadable)
return retObj;
// Update the message list index variables so that the message list is in
// the right spot for the message currently being read
this.CalcMsgListScreenIdxVarsFromMsgNum(pOffset+1);
// Check the pAllowChgArea parameter. If it's a boolean, then use it. If
// not, then check to see if we're reading personal mail - If not, then allow
// the user to change to a different message area.
var allowChgMsgArea = true;
if (typeof(pAllowChgArea) == "boolean")
allowChgMsgArea = pAllowChgArea;
else
allowChgMsgArea = (this.subBoardCode != "mail");

nightfox
committed
// Get the message text and see if it has any ANSI codes. Remove any pause
// codes it might have. If it has ANSI codes, then don't use the scrolling
// interface so that the ANSI gets displayed properly.
var messageText = this.GetMsgBody(msgHeader);
if (msgHdrHasAttachmentFlag(msgHeader))
{
messageText = "\x01n\x01g\x01h- This message contains one or more attachments. Press CTRL-A to download.\x01n\r\n"
+ "\x01n\x01g\x01h--------------------------------------------------------------------------\x01n\r\n"
+ messageText;
}
var useScrollingInterface = this.scrollingReaderInterface && console.term_supports(USER_ANSI);
// If we switch to the non-scrolling interface here, then the calling method should
// refresh the enhanced reader help line on the screen.
retObj.refreshEnhancedRdrHelpLine = (this.scrollingReaderInterface && !useScrollingInterface);
// If the current message is new to the user, update the number of posts read this session.
if (pOffset > this.GetScanPtrMsgIdx())
++bbs.posts_read;
// Use the scrollable reader interface if the setting is enabled & the user's
// terminal supports ANSI. Otherwise, use a more traditional user interface.
if (useScrollingInterface)
retObj = this.ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgArea, messageText, pOffset);
else
retObj = this.ReadMessageEnhanced_Traditional(msgHeader, allowChgMsgArea, messageText, pOffset);
// Mark the message as read if it was written to the current user
if (userNameHandleAliasMatch(msgHeader.to) && ((msgHeader.attr & MSG_READ) == 0))
{
// Using applyAttrsInMsgHdrInMessagbase(), which loads the header without
// expanded fields and saves the attributes with that header.
var saveRetObj = applyAttrsInMsgHdrInMessagbase(this.subBoardCode, msgHeader.number, MSG_READ);
if (this.SearchTypePopulatesSearchResults() && saveRetObj.saveSucceeded)
this.RefreshHdrInSavedArrays(pOffset, MSG_READ, true);
}
// If not reading personal email and not doing a search, then update the
// scan & last read message pointers.
if ((this.subBoardCode != "mail") && (this.searchType == SEARCH_NONE))
{
if (msgHeader.number > GetScanPtrOrLastMsgNum(this.subBoardCode))
msg_area.sub[this.subBoardCode].scan_ptr = msgHeader.number;
msg_area.sub[this.subBoardCode].last_read = msgHeader.number;
}
return retObj;
}
// Helper method for ReadMessageEnhanced() - Does the scrollable reader interface
function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgArea, messageText, pOffset)
{
var retObj = {
offsetValid: true,
msgNotReadable: false,
userReplied: false,
lastKeypress: "",
newMsgOffset: -1,
nextAction: ACTION_NONE,
refreshEnhancedRdrHelpLine: false
};
Eric Oulashin
committed
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
// This is a scrollbar update function for use when viewing the message.
function msgScrollbarUpdateFn(pFractionToLastPage)
{
// Update the scrollbar position for the message, depending on the
// value of pFractionToLastMessage.
fractionToLastPage = pFractionToLastPage;
solidBlockStartRow = msgReaderObj.msgAreaTop + Math.floor(numNonSolidScrollBlocks * pFractionToLastPage);
if (solidBlockStartRow != solidBlockLastStartRow)
msgReaderObj.UpdateEnhancedReaderScollbar(solidBlockStartRow, solidBlockLastStartRow, numSolidScrollBlocks);
solidBlockLastStartRow = solidBlockStartRow;
console.gotoxy(1, console.screen_rows);
}
// This is a scrollbar update function for use when viewing the header info/kludge lines.
function msgInfoScrollbarUpdateFn(pFractionToLastPage)
{
var infoSolidBlockStartRow = msgReaderObj.msgAreaTop + Math.floor(numNonSolidInfoScrollBlocks * pFractionToLastPage);
if (infoSolidBlockStartRow != lastInfoSolidBlockStartRow)
msgReaderObj.UpdateEnhancedReaderScollbar(infoSolidBlockStartRow, lastInfoSolidBlockStartRow, numInfoSolidScrollBlocks);
lastInfoSolidBlockStartRow = infoSolidBlockStartRow;
console.gotoxy(1, console.screen_rows);
}

Eric Oulashin
committed
var msgAreaWidth = this.useEnhReaderScrollbar ? this.msgAreaWidth : this.msgAreaWidth + 1;
Eric Oulashin
committed
// We could word-wrap the message to ensure words aren't split across lines, but
// doing so could make some messages look bad (i.e., messages with drawing characters),
// and word_wrap also might not handle ANSI or other color/attribute codes..
//if (!textHasDrawingChars(messageText))

Eric Oulashin
committed
// messageText = word_wrap(messageText, msgAreaWidth);
Eric Oulashin
committed
// If the message has ANSI content, then use a Graphic object to help make
// the message look good. Also, remove any ANSI clear screen codes from the
// message text.
var msgHasANSICodes = messageText.indexOf("\x1b[") >= 0;
if (msgHasANSICodes)
{
messageText = messageText.replace(/\u001b\[[012]J/gi, "");
//var graphic = new Graphic(msgAreaWidth, this.msgAreaHeight-1);
// To help ensure ANSI messages look good, it seems the Graphic object should have
// its with later set to 1 less than the width used to create it.
var graphicWidth = (msgAreaWidth < console.screen_columns ? msgAreaWidth+1 : console.screen_columns);
var graphic = new Graphic(graphicWidth, this.msgAreaHeight-1);
graphic.auto_extend = true;
graphic.ANSI = ansiterm.expand_ctrl_a(messageText);

Eric Oulashin
committed
//graphic.normalize();
graphic.width = graphicWidth - 1;

Eric Oulashin
committed
//messageText = graphic.MSG.split('\n');
messageText = graphic.MSG;
}
// Show the message header
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
// Get the message text, interpret any @-codes in it, replace tabs with spaces
// to prevent weirdness when displaying the message lines, and word-wrap the
// text so that it looks good on the screen,
var msgInfo = this.GetMsgInfoForEnhancedReader(msgHeader, true, true, true, messageText);
var topMsgLineIdxForLastPage = msgInfo.topMsgLineIdxForLastPage;
var numSolidScrollBlocks = msgInfo.numSolidScrollBlocks;
var numNonSolidScrollBlocks = msgInfo.numNonSolidScrollBlocks;
var solidBlockStartRow = msgInfo.solidBlockStartRow;
var solidBlockLastStartRow = solidBlockStartRow;
var topMsgLineIdx = 0;
var fractionToLastPage = 0;
if (topMsgLineIdxForLastPage != 0)
fractionToLastPage = topMsgLineIdx / topMsgLineIdxForLastPage;

Eric Oulashin
committed
// If use of the scrollbar is enabled, draw an initial scrollbar on the rightmost
// column of the message area showing the fraction of the message shown and what
// part of the message is currently being shown. The scrollbar will be updated
// minimally in the input loop to minimize screen redraws.
if (this.useEnhReaderScrollbar)
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
// Input loop (for scrolling the message up & down)

Eric Oulashin
committed
var msgLineFormatStr = "%-" + msgAreaWidth + "s";
var writeMessage = true;
// msgAreaHeight, msgReaderObj, and scrollbarUpdateFunction are for use
// with scrollTextLines().
var msgAreaHeight = this.msgAreaBottom - this.msgAreaTop + 1;
var msgReaderObj = this;
var msgHasAttachments = msgHdrHasAttachmentFlag(msgHeader);
// User input loop
var continueOn = true;
while (continueOn)
{
// Display the message lines (depending on the value of writeMessage)
// and handle scroll keys via scrollTextLines(). Handle other keypresses
// here.
Eric Oulashin
committed
var scrollbarInfoObj = {
solidBlockLastStartRow: 0,
numSolidScrollBlocks: 0
};
scrollbarInfoObj.solidBlockLastStartRow = solidBlockLastStartRow;
scrollbarInfoObj.numSolidScrollBlocks = numSolidScrollBlocks;
var scrollRetObj = scrollTextLines(msgInfo.messageLines, topMsgLineIdx,
this.colors.msgBodyColor, writeMessage,

Eric Oulashin
committed
this.msgAreaLeft, this.msgAreaTop, msgAreaWidth,
msgAreaHeight, 1, console.screen_rows, this.useEnhReaderScrollbar,

Eric Oulashin
committed
msgScrollbarUpdateFn, scrollbarInfoObj);
topMsgLineIdx = scrollRetObj.topLineIdx;
retObj.lastKeypress = scrollRetObj.lastKeypress;
switch (retObj.lastKeypress)
{
case this.enhReaderKeys.deleteMessage: // Delete message

Eric Oulashin
committed
case '\x7f':
case '\x08':
var originalCurpos = console.getxy();
// The 2nd to last row of the screen is where the user will
// be prompted for confirmation to delete the message.
// Ideally, I'd like to put the cursor on the last row of
// the screen for this, but console.noyes() lets the enter
// key shift everything on screen up one row, and there's
// no way to avoid that. So, to optimize screen refreshing,
// the cursor is placed on the 2nd to the last row on the
// screen to prompt for confirmation.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
// Prompt the user for confirmation to delete the message.
// Note: this.PromptAndDeleteOrUndeleteMessage() will check to see if the user
// is a sysop or the message was posted by the user.
// If the message was deleted and the user can't view deleted messages,
// then exit this read method and return KEY_RIGHT as the last keypress
// so that the calling method will go to the next message/sub-board.
// Otherwise (if the message was not deleted), refresh the
// last 2 lines of the message on the screen.

Eric Oulashin
committed
var msgWasDeleted = this.PromptAndDeleteOrUndeleteMessage(pOffset, promptPos, true, true, msgAreaWidth, true);
if (msgWasDeleted && !canViewDeletedMsgs())
{
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
continueOn = msgSearchObj.continueInputLoop;
retObj.newMsgOffset = msgSearchObj.newMsgOffset;
retObj.nextAction = msgSearchObj.nextAction;
if (msgSearchObj.promptGoToNextArea)
{
Eric Oulashin
committed
if (this.EnhReaderPromptYesNo(replaceAtCodesInStr(this.text.goToNextMsgAreaPromptText), msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = false; // No need to refresh the message
}
}
else
{
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
// Move the cursor back to its original position
console.gotoxy(originalCurpos);
writeMessage = false;
}
break;
case this.enhReaderKeys.selectMessage: // Select message (for batch delete, etc.)
var originalCurpos = console.getxy();
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
if (this.EnhReaderPromptYesNo("Select this message", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks, true))
this.ToggleSelectedMessage(this.subBoardCode, pOffset, true);
else
this.ToggleSelectedMessage(this.subBoardCode, pOffset, false);
writeMessage = false; // No need to refresh the message
break;
case this.enhReaderKeys.batchDelete:
// TODO: Write this? Not sure yet if it makes much sense to
// have batch delete in the reader interface.
// Prompt the user for confirmation, and use
// this.DeleteOrUndeleteSelectedMessages() to mark the selected messages
// as deleted.
// Returns an object with the following properties:
// deletedAll: Boolean - Whether or not all messages were successfully marked
// for deletion
// failureList: An object containing indexes of messages that failed to get
// marked for deletion, indexed by internal sub-board code, then
// containing messages indexes as properties. Reasons for failing
// to mark messages deleted can include the user not having permission
// to delete in a sub-board, failure to open the sub-board, etc.
writeMessage = false; // No need to refresh the message
break;

nightfox
committed
case this.enhReaderKeys.editMsg: // Edit the messaage
if (this.CanEdit())
{
// Move the cursor to the last line in the message area so
// the edit confirmation prompt will appear there. Not using
// the last line on the screen because the yes/no prompt will
// output a carriage return and move everything on the screen
// up one line, which is not ideal in case the user says No.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
// Let the user edit the message if they want to
var editReturnObj = this.EditExistingMsg(pOffset);
// If the user didn't confirm, then we only have to refresh the bottom
// help line. Otherwise, we need to refresh everything on the screen.
if (!editReturnObj.userConfirmed)
{
// For some reason, the yes/no prompt erases the last character
// of the scrollbar - So, figure out which block was there and
// refresh it.
//var scrollBarBlock = "\x01n\x01h\x01k" + BLOCK1; // Dim block
// Dim block

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{

Eric Oulashin
committed
var scrollBarBlock = this.colors.scrollbarBGColor + this.text.scrollbarBGChar;
if (solidBlockStartRow + numSolidScrollBlocks - 1 == this.msgAreaBottom)
{
//scrollBarBlock = "\x01w" + BLOCK2; // Bright block
// Bright block
scrollBarBlock = this.colors.scrollbarScrollBlockColor + this.text.scrollbarScrollBlockChar;
}
else
{
// TODO
}
console.gotoxy(this.msgAreaRight+1, this.msgAreaBottom);
console.print(scrollBarBlock);
}
// Refresh the last 2 message lines on the screen, then display
// the key help line
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
writeMessage = false;
}
else
{
// If the message was edited, then refresh the text lines
// array and update the other message-related variables.
if (editReturnObj.msgEdited && (editReturnObj.newMsgIdx > -1))
{
// When the message is edited, the old message will be
// deleted and the edited message will be posted as a new
// message. So we should return to the caller and have it
// go directly to that new message.
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
continueOn = false;
retObj.newMsgOffset = editReturnObj.newMsgIdx;
}
else
{
// The message was not edited. Refresh everything on the screen.
// If the enhanced message header width is less than the console
// width, then clear the screen to remove anything that might be
// left on the screen by the message editor.
if (this.enhMsgHeaderWidth < console.screen_columns)
console.clear("\x01n");
// Display the message header and key help line
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again, and ensure it's in the correct position

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
}
}
}
else
writeMessage = false; // Don't write the current message again
break;

nightfox
committed
case this.enhReaderKeys.showHelp: // Show the help screen
this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgHasAttachments);
// If the enhanced message header width is less than the console
// width, then clear the screen to remove anything left on the
// screen from the help screen.
if (this.enhMsgHeaderWidth < console.screen_columns)
console.clear("\x01n");
// Display the message header and key help line
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again, and ensure it's in the correct position

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
break;
case this.enhReaderKeys.reply: // Reply to the message
case this.enhReaderKeys.privateReply: // Private message reply
// If the user pressed the private reply key while reading private
// mail, then do nothing (allow only the regular reply key to reply).
var privateReply = (retObj.lastKeypress == this.enhReaderKeys.privateReply);
if (privateReply && this.readingPersonalEmail)
writeMessage = false; // Don't re-write the current message again
else
{
// Get the message header with fields expanded so we can get the most info possible.
//var extdMsgHdr = this.GetMsgHdrByAbsoluteNum(msgHeader.number, true);
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true);
msgbase.close();
// Let the user reply to the message.
var replyRetObj = this.ReplyToMsg(extdMsgHdr, messageText, privateReply, pOffset);
retObj.userReplied = replyRetObj.postSucceeded;
//retObj.msgNotReadable = replyRetObj.msgWasDeleted;
var msgWasDeleted = replyRetObj.msgWasDeleted;
//if (retObj.msgNotReadable)
if (msgWasDeleted && !canViewDeletedMsgs())
{
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
continueOn = msgSearchObj.continueInputLoop;
retObj.newMsgOffset = msgSearchObj.newMsgOffset;
retObj.nextAction = msgSearchObj.nextAction;
if (msgSearchObj.promptGoToNextArea)
{
Eric Oulashin
committed
if (this.EnhReaderPromptYesNo(replaceAtCodesInStr(this.text.goToNextMsgAreaPromptText), msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks))
{
// Let this method exit and let the caller go to the next sub-board
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
writeMessage = true; // We want to refresh the message on the screen
}
}
else
{
// If the enhanced message header width is less than the console
// width, then clear the screen to remove anything left on the
// screen by the message editor.
if (this.enhMsgHeaderWidth < console.screen_columns)
console.clear("\x01n");
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
}
}
}
break;

nightfox
committed
case this.enhReaderKeys.postMsg: // Post a message
if (!this.readingPersonalEmail)
{
// Let the user post a message.
if (bbs.post_msg(this.subBoardCode))
{
if (searchTypePopulatesSearchResults(this.searchType))
{
// TODO: If the user is doing a search, it might be
// useful to search their new message and add it to
// the search results if it's a match.. but maybe
// not?
}
console.pause();
}
// Refresh things on the screen
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
writeMessage = true; // We want to refresh the message on the screen
}
else
writeMessage = false; // Don't re-write the current message again
break;
// Numeric digit: The start of a number of a message to read
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
var originalCurpos = console.getxy();
// Put the user's input back in the input buffer to
// be used for getting the rest of the message number.
console.ungetstr(retObj.lastKeypress);
// Move the cursor to the 2nd to last row of the screen and
// prompt the user for the message number. Ideally, I'd like
// to put the cursor on the last row of the screen for this, but
// console.getnum() lets the enter key shift everything on screen
// up one row, and there's no way to avoid that. So, to optimize
// screen refreshing, the cursor is placed on the 2nd to the last
// row on the screen to prompt for the message number.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
// Prompt for the message number
Eric Oulashin
committed
var msgNumInput = this.PromptForMsgNum(promptPos, replaceAtCodesInStr(this.text.readMsgNumPromptText), false, ERROR_PAUSE_WAIT_MS, false);
// Only allow reading the message if the message number is valid
// and it's not the same message number that was passed in.
if ((msgNumInput > 0) && (msgNumInput-1 != pOffset))
{
// If the message is marked as deleted, then output an error
if (this.MessageIsDeleted(msgNumInput-1))
{
writeWithPause(this.msgAreaLeft, console.screen_rows-1,
"\x01n" + replaceAtCodesInStr(format(this.text.msgHasBeenDeletedText, msgNumInput)) + "\x01n",
ERROR_PAUSE_WAIT_MS, "\x01n", true);
}
else
{
// Confirm with the user whether to read the message
var readMsg = true;
if (this.promptToReadMessage)
{
var sReadMsgConfirmText = this.colors["readMsgConfirmColor"]
+ "Read message "
+ this.colors["readMsgConfirmNumberColor"]
+ msgNumInput + this.colors["readMsgConfirmColor"]
+ ": Are you sure";
console.gotoxy(promptPos);
console.print("\x01n");
readMsg = console.yesno(sReadMsgConfirmText);
}
if (readMsg)
{
continueOn = false;
retObj.newMsgOffset = msgNumInput - 1;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
}
else
writeMessage = false; // Don't re-write the current message again
}
}
else // Message number invalid or the same as what was passed in
writeMessage = false; // Don't re-write the current message again
// If the user chose to continue reading messages, then refresh
// the last 2 message lines in the last part of the message area
// and then put the cursor back to its original position.
if (continueOn)
{
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
// Move the cursor back to its original position
console.gotoxy(originalCurpos);
}
break;
case this.enhReaderKeys.prevMsgByTitle: // Previous message by title
case this.enhReaderKeys.prevMsgByAuthor: // Previous message by author
case this.enhReaderKeys.prevMsgByToUser: // Previous message by 'to user'
case this.enhReaderKeys.prevMsgByThreadID: // Previous message by thread ID
// Only allow this if we aren't doing a message search.
if (!this.SearchingAndResultObjsDefinedForCurSub())
{
var threadPrevMsgOffset = this.FindThreadPrevOffset(msgHeader,
keypressToThreadType(retObj.lastKeypress, this.enhReaderKeys),
true);
if (threadPrevMsgOffset > -1)
{
retObj.newMsgOffset = threadPrevMsgOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
}
else
{
// Refresh the help line at the bottom of the screen
//this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
writeMessage = false; // Don't re-write the current message again
}
// Make sure the help line on the bottom of the screen is
// drawn.
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
writeMessage = false; // Don't re-write the current message again
break;
case this.enhReaderKeys.nextMsgByTitle: // Next message by title (subject)
case this.enhReaderKeys.nextMsgByAuthor: // Next message by author
case this.enhReaderKeys.nextMsgByToUser: // Next message by 'to user'
case this.enhReaderKeys.nextMsgByThreadID: // Next message by thread ID
// Only allow this if we aren't doing a message search.
if (!this.SearchingAndResultObjsDefinedForCurSub())
{
var threadPrevMsgOffset = this.FindThreadNextOffset(msgHeader,
keypressToThreadType(retObj.lastKeypress, this.enhReaderKeys),
true);
if (threadPrevMsgOffset > -1)
{
retObj.newMsgOffset = threadPrevMsgOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
}
else
writeMessage = false; // Don't re-write the current message again
// Make sure the help line on the bottom of the screen is
// drawn.
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
writeMessage = false; // Don't re-write the current message again
break;

nightfox
committed
case this.enhReaderKeys.previousMsg: // Previous message
// Look for a prior message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the previous message sub-board/group.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, false);
// As a screen redraw optimization: Only return if there is a valid new
// message offset or the user is allowed to change to a different sub-board.
// Otherwise, don't return, and don't refresh the message on the screen.
var goToPrevMessage = false;
if ((retObj.newMsgOffset > -1) || allowChgMsgArea)
{
if (retObj.newMsgOffset == -1 && !curMsgSubBoardIsLast())
{
Eric Oulashin
committed
goToPrevMessage = this.EnhReaderPromptYesNo(replaceAtCodesInStr(this.text.goToPrevMsgAreaPromptText),
msgInfo.messageLines, topMsgLineIdx,
msgLineFormatStr, solidBlockStartRow,
numSolidScrollBlocks);
}
else
{
// We're not at the beginning of the sub-board, so it's okay to exit this
// method and go to the previous message.
goToPrevMessage = true;
}
}
if (goToPrevMessage)
{
continueOn = false;
retObj.nextAction = ACTION_GO_PREVIOUS_MSG;
}
else
writeMessage = false; // No need to refresh the message
break;

nightfox
committed
case this.enhReaderKeys.nextMsg: // Next message
case KEY_ENTER:
// Look for a later message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the next message sub-board/group.

Eric Oulashin
committed
var findNextMsgRetObj = this.ScrollableReaderNextReadableMessage(pOffset, msgInfo, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks);
if (findNextMsgRetObj.newMsgOffset > -1)
retObj.newMsgOffset = findNextMsgRetObj.newMsgOffset;
writeMessage = findNextMsgRetObj.writeMessage;
continueOn = findNextMsgRetObj.continueOn;
retObj.nextAction = findNextMsgRetObj.nextAction;
break;
// First & last message: Quit out of this input loop and let the
// calling function, this.ReadMessages(), handle the action.

nightfox
committed
case this.enhReaderKeys.firstMsg: // First message
// Only leave this function if we aren't already on the first message.
if (pOffset > 0)
{
continueOn = false;
retObj.nextAction = ACTION_GO_FIRST_MSG;
}
else
writeMessage = false; // Don't re-write the current message again
break;

nightfox
committed
case this.enhReaderKeys.lastMsg: // Last message
// Only leave this function if we aren't already on the last message.
if (pOffset < this.NumMessages() - 1)
{
continueOn = false;
retObj.nextAction = ACTION_GO_LAST_MSG;
}
else
writeMessage = false; // Don't re-write the current message again
break;
case this.enhReaderKeys.prevSubBoard: // Go to the previous message area
if (allowChgMsgArea)
{
continueOn = false;
retObj.nextAction = ACTION_GO_PREV_MSG_AREA;
}
else
writeMessage = false; // Don't re-write the current message again
break;
case this.enhReaderKeys.nextSubBoard: // Go to the next message area
if (allowChgMsgArea || this.doingMultiSubBoardScan)
{
continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
}
else
writeMessage = false; // Don't re-write the current message again
break;
// H and K: Display the extended message header info/kludge lines
// (for the sysop)

nightfox
committed
case this.enhReaderKeys.showHdrInfo:
case this.enhReaderKeys.showKludgeLines:
Eric Oulashin
committed
if (user.is_sysop)
{
// Save the original cursor position
var originalCurPos = console.getxy();
// Get an array of the extended header info/kludge lines and then
// allow the user to scroll through them.

nightfox
committed
var extdHdrInfoLines = this.GetExtdMsgHdrInfo(msgHeader, (retObj.lastKeypress == this.enhReaderKeys.showKludgeLines));
if (extdHdrInfoLines.length > 0)
{

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
// Calculate information for the scrollbar for the kludge lines
var infoFractionShown = this.msgAreaHeight / extdHdrInfoLines.length;
if (infoFractionShown > 1)
infoFractionShown = 1.0;
var numInfoSolidScrollBlocks = Math.floor(this.msgAreaHeight * infoFractionShown);
if (numInfoSolidScrollBlocks == 0)
numInfoSolidScrollBlocks = 1;
var numNonSolidInfoScrollBlocks = this.msgAreaHeight - numInfoSolidScrollBlocks;
var lastInfoSolidBlockStartRow = this.msgAreaTop;
// Display the kludge lines and let the user scroll through them
this.DisplayEnhancedReaderWholeScrollbar(this.msgAreaTop, numInfoSolidScrollBlocks);
}
scrollTextLines(extdHdrInfoLines, 0, this.colors["msgBodyColor"], true, this.msgAreaLeft,

Eric Oulashin
committed
this.msgAreaTop, msgAreaWidth, msgAreaHeight, 1, console.screen_rows,
this.useEnhReaderScrollbar, msgInfoScrollbarUpdateFn);
// Display the scrollbar for the message to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
}
else
{
// There are no kludge lines for this message
Eric Oulashin
committed
this.DisplayEnhReaderError(replaceAtCodesInStr(this.text.noKludgeLinesForThisMsgText), msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
console.gotoxy(originalCurPos);
writeMessage = false;
}
}
else // The user is not a sysop
writeMessage = false;
break;
// Message list, change message area: Quit out of this input loop
// and let the calling function, this.ReadMessages(), handle the
// action.

nightfox
committed
case this.enhReaderKeys.showMsgList: // Message list
console.print("\x01n");
console.crlf();
console.print("Loading...");
retObj.nextAction = ACTION_DISPLAY_MSG_LIST;
continueOn = false;
break;

nightfox
committed
case this.enhReaderKeys.chgMsgArea: // Change message area, if allowed
if (allowChgMsgArea)
{
retObj.nextAction = ACTION_CHG_MSG_AREA;
continueOn = false;
}
else
writeMessage = false; // No need to refresh the message
break;
case this.enhReaderKeys.downloadAttachments: // Download attachments
if (msgHasAttachments)
{
console.print("\x01n");
console.gotoxy(1, console.screen_rows);
console.crlf();
allowUserToDownloadMessage_NewInterface(msgHeader, this.subBoardCode);
// Refresh things on the screen
console.clear("\x01n");
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
}
else
writeMessage = false;
break;
case this.enhReaderKeys.saveToBBSMachine:
// Save the message to the BBS machine - Only allow this
// if the user is a sysop.
Eric Oulashin
committed
if (user.is_sysop)
{
// Prompt the user for a filename to save the message to the
// BBS machine
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
console.print("\x01n\x01cFilename:\x01h");
var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1
var filename = console.getstr(inputLen, K_NOCRLF);
console.print("\x01n");
if (filename.length > 0)
{
var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename);
console.gotoxy(promptPos);
console.cleartoeol("\x01n");
console.gotoxy(promptPos);
if (saveMsgRetObj.succeeded)
var statusMsg = "\x01n\x01cThe message has been saved.";
if (msgHdrHasAttachmentFlag(msgHeader))
statusMsg += " Attachments not saved.";
statusMsg += "\x01n";
console.print(statusMsg);
}
else
console.print("\x01n\x01y\x01hFailed: " + saveMsgRetObj.errorMsg + "\x01n");
mswait(ERROR_PAUSE_WAIT_MS);
}
else
{
console.gotoxy(promptPos);
console.print("\x01n\x01y\x01hMessage not exported\x01n");
mswait(ERROR_PAUSE_WAIT_MS);
}
// Refresh the last 2 lines of the message on the screen to overwrite
// the file save prompt
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
}

Eric Oulashin
committed
writeMessage = false; // Don't write the whole message again
break;

nightfox
committed
case this.enhReaderKeys.userEdit: // Edit the user who wrote the message
Eric Oulashin
committed
if (user.is_sysop)
{
console.print("\x01n");
console.crlf();
console.print("- Edit user " + msgHeader.from);
console.crlf();
var editObj = editUser(msgHeader.from);
if (editObj.errorMsg.length != 0)
{
console.print("\x01n");
console.crlf();
console.print("\x01y\x01h" + editObj.errorMsg + "\x01n");
console.crlf();
console.pause();
}
// Refresh things on the screen
console.clear("\x01n");
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
}
else // The user is not a sysop
writeMessage = false;
break;

nightfox
committed
case this.enhReaderKeys.forwardMsg: // Forward the message
console.print("\x01n");

nightfox
committed
console.crlf();
console.print("\x01c- Forward message\x01n");

nightfox
committed
console.crlf();
var retStr = this.ForwardMessage(msgHeader, messageText);
if (retStr.length > 0)
{
console.print("\x01n\x01h\x01y* " + retStr + "\x01n");

nightfox
committed
console.crlf();
console.pause();
}
// Refresh things on the screen
console.clear("\x01n");

nightfox
committed
// Display the message header and key help line again
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
// Display the scrollbar again to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}

nightfox
committed
writeMessage = true; // We want to refresh the message on the screen
break;
case this.enhReaderKeys.vote: // Vote on the message
// Move the cursor to the last line in the message area so the
// vote question text prompt will appear there.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
console.gotoxy(1, console.screen_rows-1);
// Let the user vote on the message
var voteRetObj = this.VoteOnMessage(msgHeader, true);
if (voteRetObj.BBSHasVoteFunction)
{
var msgIsPollVote = ((typeof(MSG_TYPE_POLL) != "undefined") && (msgHeader.type & MSG_TYPE_POLL) == MSG_TYPE_POLL);
if (!voteRetObj.userQuit)
{
// If the message is a poll vote, then output any error
// message on its own line and refresh the whole screen.
// Otherwise, use the last 2 rows for an error message
// and only refresh what's necessary.
if (msgIsPollVote)
console.print("\x01n");
console.gotoxy(1, console.screen_rows-1);
if (voteRetObj.errorMsg.length > 0)
{
if (voteRetObj.mnemonicsRequiredForErrorMsg)
{
console.mnemonics(voteRetObj.errorMsg);
console.print("\x01n");
}
else
console.print("\x01y\x01h* " + voteRetObj.errorMsg + "\x01n");
mswait(ERROR_PAUSE_WAIT_MS);
}
else if (!voteRetObj.savedVote)
{
console.print("\x01y\x01h* Failed to save the vote\x01n");
mswait(ERROR_PAUSE_WAIT_MS);
}
}
else
{
// Not a poll vote - Just an up/down vote
if ((voteRetObj.errorMsg.length > 0) || (!voteRetObj.savedVote))
{
console.print("\x01n");
console.gotoxy(1, console.screen_rows-1);
if (voteRetObj.errorMsg.length > 0)
{
if (voteRetObj.mnemonicsRequiredForErrorMsg)
{
console.mnemonics(voteRetObj.errorMsg);
console.print("\x01n");
}
else
console.print("\x01y\x01h* " + voteRetObj.errorMsg + "\x01n");
}
else if (!voteRetObj.savedVote)
console.print("\x01y\x01h* Failed to save the vote\x01n");
}
else
msgHeader = voteRetObj.updatedHdr; // To get updated vote information
mswait(ERROR_PAUSE_WAIT_MS);
writeMessage = false;
}
// Exit out of the reader and come back to read
// the same message again so that the voting results
// are re-loaded and displayed on the screen.
retObj.newMsgOffset = pOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
{
// The user quit out of voting. Refresh the screen.
// Exit out of the reader and come back to read
// the same message again so that the screen is refreshed.
retObj.newMsgOffset = pOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
}
else
writeMessage = false;
break;
case this.enhReaderKeys.showVotes: // Show votes
// Save the original cursor position
var originalCurPos = console.getxy();
if (msgHeader.hasOwnProperty("total_votes") && msgHeader.hasOwnProperty("upvotes"))
{

nightfox
committed
var voteInfo = this.GetUpvoteAndDownvoteInfo(msgHeader);
// Display the vote info and let the user scroll through them
// (the console height should be enough, but do this just in case)
// Calculate information for the scrollbar for the vote info lines

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
var infoFractionShown = this.msgAreaHeight / voteInfo.length;
if (infoFractionShown > 1)
infoFractionShown = 1.0;
var numInfoSolidScrollBlocks = Math.floor(this.msgAreaHeight * infoFractionShown);
if (numInfoSolidScrollBlocks == 0)
numInfoSolidScrollBlocks = 1;
var numNonSolidInfoScrollBlocks = this.msgAreaHeight - numInfoSolidScrollBlocks;
var lastInfoSolidBlockStartRow = this.msgAreaTop;
// Display the vote info lines and let the user scroll through them
this.DisplayEnhancedReaderWholeScrollbar(this.msgAreaTop, numInfoSolidScrollBlocks);
}
else
{
// TODO
}
scrollTextLines(voteInfo, 0, this.colors["msgBodyColor"], true, this.msgAreaLeft, this.msgAreaTop, msgAreaWidth,
msgAreaHeight, 1, console.screen_rows, this.useEnhReaderScrollbar, msgInfoScrollbarUpdateFn);
// Display the scrollbar for the message to refresh it on the screen

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// TODO
}
writeMessage = true; // We want to refresh the message on the screen
}
else
{
this.DisplayEnhReaderError("There is no voting information for this message", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
console.gotoxy(originalCurPos);
writeMessage = false;
}
break;
case this.enhReaderKeys.closePoll: // Close a poll message
// Save the original cursor position
var originalCurPos = console.getxy();
var pollCloseMsg = "";
// If this message is a poll, then allow closing it.
if ((typeof(MSG_TYPE_POLL) != "undefined") && (msgHeader.type & MSG_TYPE_POLL) == MSG_TYPE_POLL)
{
if ((msgHeader.auxattr & POLL_CLOSED) == 0)
{
// Only let the user close the poll if they created it
if (userHandleAliasNameMatch(msgHeader.from))
{
// Prompt to confirm whether the user wants to close the poll
console.gotoxy(1, console.screen_rows-1);
printf("\x01n%" + +(console.screen_columns-1) + "s", "");
console.gotoxy(1, console.screen_rows-1);
if (!console.noyes("Close poll"))
{
// Close the poll (open the sub-board first)
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
if (closePollWithOpenMsgbase(msgbase, msgHeader.number))
{
msgHeader.auxattr |= POLL_CLOSED;
pollCloseMsg = "\x01n\x01cThis poll was successfully closed.";
}
else
pollCloseMsg = "\x01n\x01r\x01h* Failed to close this poll!";
msgbase.close();
}
else
pollCloseMsg = "\x01n\x01y\x01hUnable to open sub-board to close the poll";
}
}
else
pollCloseMsg = "\x01n\x01y\x01hCan't close this poll because it's not yours";
}
else
pollCloseMsg = "\x01n\x01y\x01hThis poll is already closed";
}
else
pollCloseMsg = "This message is not a poll";
// Display the poll closing status message
this.DisplayEnhReaderError(pollCloseMsg, msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
console.gotoxy(originalCurPos);
writeMessage = false;
break;
case this.enhReaderKeys.validateMsg: // Validate the message
Eric Oulashin
committed
if (user.is_sysop && (this.subBoardCode != "mail") && msg_area.sub[this.subBoardCode].is_moderated)
{
var message = "";
if (this.ValidateMsg(this.subBoardCode, msgHeader.number))
{
message = "\x01n\x01cMessage validation successful";
// Refresh the message header in the arrays
this.RefreshMsgHdrInArrays(msgHeader.number);
// Exit out of the reader and come back to read
// the same message again so that the voting results
// are re-loaded and displayed on the screen.
retObj.newMsgOffset = pOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
continueOn = false;
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else
message = "\x01n\x01y\x01hMessage validation failed!";
this.DisplayEnhReaderError(message, msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
}
else
writeMessage = false;
break;
case this.enhReaderKeys.bypassSubBoardInNewScan:
writeMessage = false; // TODO: Finish
/*
if (this.doingMsgScan)
{
console.print("\x01n");
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
var originalCurpos = console.getxy();
// The 2nd to last row of the screen is where the user will
// be prompted for confirmation to delete the message.
// Ideally, I'd like to put the cursor on the last row of
// the screen for this, but console.noyes() lets the enter
// key shift everything on screen up one row, and there's
// no way to avoid that. So, to optimize screen refreshing,
// the cursor is placed on the 2nd to the last row on the
// screen to prompt for confirmation.
var promptPos = this.EnhReaderPrepLast2LinesForPrompt();
if (!console.noyes("Bypass this sub-board in newscans"))
{
continueOn = false;
msg_area.sub[this.subBoardCode].scan_cfg &= SCAN_CFG_NEW;
}
else
{
this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
// Move the cursor back to its original position
console.gotoxy(originalCurpos);
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
}
else
writeMessage = false;
*/
break;

Eric Oulashin
committed
case this.enhReaderKeys.userSettings:

Eric Oulashin
committed
// Make a backup copy of the this.useEnhReaderScrollbar setting in case it changes, so we can tell if we need to refresh the scrollbar
var oldUseEnhReaderScrollbar = this.useEnhReaderScrollbar;
var userSettingsRetObj = this.DoUserSettings_Scrollable(function(pReader) { pReader.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea); });

Eric Oulashin
committed
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
retObj.lastKeypress = "";
writeMessage = userSettingsRetObj.needWholeScreenRefresh;
// In case the user changed their twitlist, re-filter the messages for this sub-board
if (userSettingsRetObj.userTwitListChanged)
{
console.gotoxy(1, console.screen_rows);
console.crlf();
console.print("\x01nTwitlist changed; re-filtering..");
var tmpMsgbase = new MsgBase(this.subBoardCode);
if (tmpMsgbase.open())
{
continueOn = false;
writeMessage = false;
var tmpAllMsgHdrs = tmpMsgbase.get_all_msg_headers(true);
tmpMsgbase.close();
this.FilterMsgHdrsIntoHdrsForCurrentSubBoard(tmpAllMsgHdrs, true);
// If the user is currently reading a message a message by someone who is now
// in their twit list, change the message currently being viewed.
if (this.MsgHdrFromOrToInUserTwitlist(msgHeader))
{
var findNextMsgRetObj = this.ScrollableReaderNextReadableMessage(pOffset, msgInfo, topMsgLineIdx, msgLineFormatStr, solidBlockStartRow, numSolidScrollBlocks);
if (findNextMsgRetObj.newMsgOffset > -1)
{
retObj.newMsgOffset = findNextMsgRetObj.newMsgOffset;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
}
else
retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
}
else
{
// If there are still messages in this sub-board, and the message offset is beyond the last
// message, then show the last message in the sub-board. Otherwise, go to the next message area.
if (this.hdrsForCurrentSubBoard.length > 0)
{
if (pOffset > this.hdrsForCurrentSubBoard.length)
{
//this.hdrsForCurrentSubBoard[this.hdrsForCurrentSubBoard.length-1].number
retObj.newMsgOffset = this.hdrsForCurrentSubBoard.length - 1;
retObj.nextAction = ACTION_GO_SPECIFIC_MSG;
}
}
else
retObj.nextAction = ACTION_GO_NEXT_MSG_AREA;
}
}
else
console.print("\x01y\x01hFailed to open the messagbase!\x01\r\n\x01p");
this.SetUpLightbarMsgListVars();
writeMessage = true;
}
if (userSettingsRetObj.needWholeScreenRefresh)
{
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);

Eric Oulashin
committed
if (this.useEnhReaderScrollbar)
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
else
{
// TODO
}

Eric Oulashin
committed
this.DisplayEnhancedMsgReadHelpLine(console.screen_rows, allowChgMsgArea);
}
else

Eric Oulashin
committed
{
// If the message scrollbar was toggled, then draw/erase the scrollbar
if (this.useEnhReaderScrollbar != oldUseEnhReaderScrollbar)
{
msgAreaWidth = this.useEnhReaderScrollbar ? this.msgAreaWidth : this.msgAreaWidth + 1;
// If the message is ANSI, then re-create the Graphic object to account for the
// new width
if (msgHasANSICodes)
{
//var graphic = new Graphic(msgAreaWidth, this.msgAreaHeight-1);
// To help ensure ANSI messages look good, it seems the Graphic object should have
// its with later set to 1 less than the width used to create it.
var graphicWidth = (msgAreaWidth < console.screen_columns ? msgAreaWidth+1 : console.screen_columns);
var graphic = new Graphic(graphicWidth, this.msgAreaHeight-1);

Eric Oulashin
committed
graphic.auto_extend = true;
graphic.ANSI = ansiterm.expand_ctrl_a(messageText);
//graphic.width = msgAreaWidth;
graphic.width = graphicWidth - 1;

Eric Oulashin
committed
messageText = graphic.MSG;
}
// Display or erase the scrollbar
if (this.useEnhReaderScrollbar)
{
solidBlockStartRow = this.msgAreaTop + Math.floor(numNonSolidScrollBlocks * fractionToLastPage);
this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks);
}
else
{
// Erase the scrollbar
console.attributes = "N";
for (var screenY = this.msgAreaTop; screenY <= this.msgAreaBottom; ++screenY)
{
console.gotoxy(this.msgAreaRight+1, screenY);
console.print(" ");
}
}
}

Eric Oulashin
committed
this.RefreshMsgAreaRectangle(msgInfo.messageLines, topMsgLineIdx, userSettingsRetObj.optionBoxTopLeftX, userSettingsRetObj.optionBoxTopLeftY, userSettingsRetObj.optionBoxWidth, userSettingsRetObj.optionBoxHeight);

Eric Oulashin
committed
}

Eric Oulashin
committed
break;

nightfox
committed
case this.enhReaderKeys.quit: // Quit
retObj.nextAction = ACTION_QUIT;
continueOn = false;
break;
default:
writeMessage = false;
break;
}
}
return retObj;
}

Eric Oulashin
committed
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
// Helper method for ReadMessageEnhanced_Scrollable(): Determines if there is a readable message after the
// one at the given offset.
//
// Parameters:
// pOfset: The offset of the current message
// pMsgInfo: An object contaiining message information
// pTopMsgLineIdx: The index of the message line at the top of the reader area
// pMsgLineFormatStr: A format string for the message line
// pSolidBlockStartRow: The screen row of where the solid blocks start for the scrollbar
// pNumSolidScrollBlocks: The number of solid blocks in the scrollbar
//
// Return value: An object containing the following properties:
// newMsgOffset: The offset of the next readable message, if available. If not available, this will be -1.
// writeMessage: Boolean - Whether or not to write the whole message again (for the scrollable interface)
// continueOn: Boolean - Whether or not to continue with the reader input loop
// nextAction: A value indicating the next action for the reader to take after leaving the reader function
function DigDistMsgReader_ScrollableReaderNextReadableMessage(pOffset, pMsgInfo, pTopMsgLineIdx, pMsgLineFormatStr, pSolidBlockStartRow, pNumSolidScrollBlocks)
{
var retObj = {
newMsgOffset: -1,
writeMessage: true,
continueOn: true,
nextAction: ACTION_NONE
};
// Look for a later message that isn't marked for deletion. Even
// if we don't find one, we'll still want to return from this
// function (with message index -1) so that this script can go
// onto the next message sub-board/group.
retObj.newMsgOffset = this.FindNextNonDeletedMsgIdx(pOffset, true);
// Note: Unlike the left arrow key, we want to exit this method when
// navigating to the next message, regardless of whether or not the
// user is allowed to change to a different sub-board, so that processes
// that require continuation (such as new message scan) can continue.
// Still, if there are no more readable messages in the current sub-board
// (and thus the user would go onto the next message area), prompt the
// user whether they want to continue onto the next message area.
if (retObj.newMsgOffset == -1 && !curMsgSubBoardIsLast())
{
// For personal mail, don't do anything, and don't refresh the
// message. In a sub-board, ask the user if they want to go
// to the next one.
if (this.readingPersonalEmail)
retObj.writeMessage = false;
else
{
// If configured to allow the user to post in the sub-board
// instead of going to the next message area and we're not
// scanning, then do so.
if (this.readingPostOnSubBoardInsteadOfGoToNext && !this.doingMsgScan)
{
console.print("\x01n");
console.crlf();
// Ask the user if they want to post on the sub-board.
// If they say yes, then do so before exiting.
var grpNameAndDesc = this.GetGroupNameAndDesc();
if (!console.noyes(replaceAtCodesInStr(format(this.text.postOnSubBoard, grpNameAndDesc.grpName, grpNameAndDesc.grpDesc))))
bbs.post_msg(this.subBoardCode);
retObj.continueOn = false;
retObj.nextAction = ACTION_QUIT;
}
else
{
// Prompt the user whether they want to go to the next message area
if (this.EnhReaderPromptYesNo(replaceAtCodesInStr(this.text.goToNextMsgAreaPromptText), pMsgInfo.messageLines, pTopMsgLineIdx, pMsgLineFormatStr, pSolidBlockStartRow, pNumSolidScrollBlocks))
{
// Let this method exit and let the caller go to the next sub-board
retObj.continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
else
retObj.writeMessage = false; // No need to refresh the message
}
}
}
else
{
// We're not at the end of the sub-board, so it's okay to exit this
// method and go to the next message.
retObj.continueOn = false;
retObj.nextAction = ACTION_GO_NEXT_MSG;
}
return retObj;
}
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
// Helper method for ReadMessageEnhanced() - Determines the next keypress for a click
// coordinate outside the scroll area.
//
// Parameters:
// pScrollRetObj: The return object of the message scroll function
// pEnhReadHelpLineClickCoords: An array of click coordinates & action strings
//
// Return value: An object containing the following properties:
// actionStr: A string containing the next action for the enhanced reader,
// or an empty string if there was no valid action found.
function DigDistMsgReader_ScrollReaderDetermineClickCoordAction(pScrollRetObj, pEnhReadHelpLineClickCoords)
{
var retObj = {
actionStr: ""
};
for (var coordIdx = 0; coordIdx < pEnhReadHelpLineClickCoords.length; ++coordIdx)
{
if ((pScrollRetObj.mouse.x == pEnhReadHelpLineClickCoords[coordIdx].x) && (pScrollRetObj.mouse.y == pEnhReadHelpLineClickCoords[coordIdx].y))
{
// The up arrow, down arrow, PageUp, PageDown, Home, and End aren't handled
// here - Those are handled in scrollTextlines().
if (pEnhReadHelpLineClickCoords[coordIdx].actionStr == LEFT_ARROW)
retObj.actionStr = this.enhReaderKeys.previousMsg;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr == RIGHT_ARROW)
retObj.actionStr = this.enhReaderKeys.nextMsg;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("DEL") == 0)
retObj.actionStr = this.enhReaderKeys.deleteMessage;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("E)") == 0)
retObj.actionStr = this.enhReaderKeys.editMsg;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("F") == 0)
retObj.actionStr = this.enhReaderKeys.firstMsg;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("L") == 0)
retObj.actionStr = this.enhReaderKeys.lastMsg;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("R") == 0)
retObj.actionStr = this.enhReaderKeys.reply;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("C") == 0)
retObj.actionStr = this.enhReaderKeys.chgMsgArea;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("Q") == 0)
retObj.actionStr = this.enhReaderKeys.quit;
else if (pEnhReadHelpLineClickCoords[coordIdx].actionStr.indexOf("?") == 0)
retObj.actionStr = this.enhReaderKeys.showHelp;
break;
}
}
return retObj;
}
// Helper method for ReadMessageEnhanced() - Does the traditional (non-scrollable) reader interface
function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsgArea, messageText, pOffset)
{
var retObj = {
offsetValid: true,
msgNotReadable: false,
userReplied: false,
lastKeypress: "",
newMsgOffset: -1,
nextAction: ACTION_NONE,
refreshEnhancedRdrHelpLine: false
};
Eric Oulashin
committed
// We could word-wrap the message to ensure words aren't split across lines, but
// doing so could make some messages look bad (i.e., messages with drawing characters),
// and word_wrap also might not handle ANSI or other color/attribute codes..
//if (!textHasDrawingChars(messageText))
// messageText = word_wrap(messageText, this.msgAreaWidth);
var msgHasAttachments = msgHdrHasAttachmentFlag(msgHeader);

Eric Oulashin
committed
// Only interpret @-codes if the user is reading personal email and the sender is a sysop. There
// are many @-codes that do some action such as move the cursor, execute a
// script, etc., and I don't want users on message networks to do anything
// malicious to users on other BBSes.

Eric Oulashin
committed
if (this.readingPersonalEmail && msgSenderIsASysop(msgHeader))
messageText = replaceAtCodesInStr(messageText); // Or this.ParseMsgAtCodes(messageText, msgHeader) to replace only some @ codes
var msgHasANSICodes = messageText.indexOf("\x1b[") >= 0;
var msgTextWrapped = (msgHasANSICodes ? messageText : word_wrap(messageText, console.screen_columns-1));
// Generate the key help text
var keyHelpText = "\x01n\x01c\x01h#\x01n\x01b, \x01c\x01hLeft\x01n\x01b, \x01c\x01hRight\x01n\x01b, ";
if (this.CanDelete() || this.CanDeleteLastMsg())
keyHelpText += "\x01c\x01hDEL\x01b, ";
if (this.CanEdit())
keyHelpText += "\x01c\x01hE\x01y)\x01n\x01cdit\x01b, ";
keyHelpText += "\x01c\x01hF\x01y)\x01n\x01cirst\x01b, \x01c\x01hL\x01y)\x01n\x01cast\x01b, \x01c\x01hR\x01y)\x01n\x01ceply\x01b, ";
// If the user is allowed to change to a different message area, then
// include that option.
if (allowChgMsgArea)
{
// If there's room for the private reply option, then include that
// before the change area option.
if (console.screen_columns >= 89)
keyHelpText += "\x01c\x01hP\x01y)\x01n\x01crivate reply\x01b, ";
keyHelpText += "\x01c\x01hC\x01y)\x01n\x01chg area\x01b, ";
}
else
{
// The user isn't allowed to change to a different message area.
// Go ahead and include the private reply option.
keyHelpText += "\x01c\x01hP\x01y)\x01n\x01crivate reply\x01b, ";
}
keyHelpText += "\x01c\x01hQ\x01y)\x01n\x01cuit\x01b, \x01c\x01h?\x01g: \x01c";
// User input loop
var writeMessage = true;
var writePromptText = true;
var continueOn = true;
while (continueOn)
{
if (writeMessage)
{
if (console.term_supports(USER_ANSI))
console.clear("\x01n");
// Write the message header & message body to the screen
this.DisplayEnhancedMsgHdr(msgHeader, pOffset+1, 1);
console.print("\x01n" + this.colors["msgBodyColor"]);
console.putmsg(msgTextWrapped, P_NOATCODES);
}
// Write the prompt text
if (writePromptText)
Loading
Loading full blame...