Newer
Older
if (this.HasUserVotedOnMsg(pMsgHdr.number))
{
retObj.errorMsg = bbs.text(typeof(VotedAlready) != "undefined" ? VotedAlready : 780);
if (removeNLsFromVoteText)
retObj.errorMsg = retObj.errorMsg.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
retObj.mnemonicsRequiredForErrorMsg = true;
return retObj;
}
// New MsgBase method: vote_msg(). it takes a message header object
// (like save_msg), except you only need a few properties, in order of
// importarnce:
// attr: you need to have this set to MSG_UPVOTE, MSG_DOWNVOTE, or MSG_VOTE
// thread_back or reply_id: either of these must be set to indicate msg to vote on
// from: name of voter
// from_net_type and from_net_addr: if applicable
14017
14018
14019
14020
14021
14022
14023
14024
14025
14026
14027
14028
14029
14030
14031
14032
14033
14034
14035
14036
14037
14038
14039
14040
14041
14042
14043
// Do some initial setup of the header for the vote message to be
// saved to the messagebase
var voteMsgHdr = new Object();
voteMsgHdr.thread_back = pMsgHdr.number;
voteMsgHdr.reply_id = pMsgHdr.number;
voteMsgHdr.from = (this.msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias;
if (pMsgHdr.from.hasOwnProperty("from_net_type"))
{
voteMsgHdr.from_net_type = pMsgHdr.from_net_type;
if (pMsgHdr.from_net_type != NET_NONE)
voteMsgHdr.from_net_addr = user.email;
}
// Input vote options from the user differently depending on whether
// the message is a poll or not
if ((pMsgHdr.attr & MSG_POLL) == MSG_POLL)
{
if (pMsgHdr.hasOwnProperty("field_list"))
{
console.clear("\1n");
var selectHdr = bbs.text(typeof(SelectItemHdr) != "undefined" ? SelectItemHdr : 501);
printf("\1n" + selectHdr + "\1n", pMsgHdr.subject);
var optionFormatStr = "\1n\1c\1h%2d\1n\1c: \1h%s\1n";
var optionNum = 1;
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
if (pMsgHdr.field_list[fieldI].type == SMB_POLL_ANSWER)
{
printf(optionFormatStr, optionNum++, pMsgHdr.field_list[fieldI].data);
console.crlf();
}
}
console.crlf();
// Get the selection prompt text from text.dat and replace the %u or %d with
// the number 1 (default option)
var selectPromptText = bbs.text(typeof(SelectItemWhich) != "undefined" ? SelectItemWhich : 503);
selectPromptText = selectPromptText.replace(/%[uU]/, 1).replace(/%[dD]/, 1);
console.mnemonics(selectPromptText);
// Get & process the selection from the user
var maxNum = optionNum - 1;
// TODO: Update to support multiple answers from the user
var userInputNum = console.getnum(maxNum);
console.print("\1n");
//if (userInputNum == 0) // The user just pressed enter to choose the default
// userInputNum = 1;
if (userInputNum == -1) // The user chose Q to quit
retObj.userQuit = true;
else
{
// The user's answer is 0-based, so if userInputNum is positive,
// subtract 1 from it (if it's already 0, that means the user
// chose to keep the default first answer).
if (userInputNum > 0)
--userInputNum;
var votes = (1 << userInputNum);
voteMsgHdr.attr = MSG_VOTE;
voteMsgHdr.votes = votes;
}
}
}
else
// The message is not a poll - Prompt for up/downvote
if ((typeof(MSG_UPVOTE) != "undefined") && (typeof(MSG_DOWNVOTE) != "undefined"))
{
var voteAttr = 0;
// Get text line 783 to prompt for voting
var textDatText = bbs.text(typeof(VoteMsgUpDownOrQuit) != "undefined" ? VoteMsgUpDownOrQuit : 783);
if (removeNLsFromVoteText)
textDatText = textDatText.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
console.print("\1n");
console.mnemonics(textDatText);
console.print("\1n");
// Using getAllowedKeyWithMode() instead of console.getkeys() so we
// can control the input mode better, so it doesn't output a CRLF
switch (getAllowedKeyWithMode("UDQ" + KEY_UP + KEY_DOWN, K_NOCRLF|K_NOSPIN))
{
case "U":
case KEY_UP:
voteAttr = MSG_UPVOTE;
break;
case "D":
case KEY_DOWN:
voteAttr = MSG_DOWNVOTE;
break;
case "Q":
default:
retObj.userQuit = true;
break;
}
// If the user voted, then save the user's vote in the attr property
// in the header
if (voteAttr != 0)
voteMsgHdr.attr = voteAttr;
14112
14113
14114
14115
14116
14117
14118
14119
14120
14121
14122
14123
14124
14125
14126
14127
14128
14129
14130
}
else
retObj.errorMsg = "MSG_UPVOTE & MSG_DOWNVOTE are not defined";
}
// If the user hasn't quit and there is no error message, then save the vote
// message header
if (!retObj.userQuit && (retObj.errorMsg.length == 0))
{
retObj.savedVote = this.msgbase.vote_msg(voteMsgHdr);
// If the save was successful, then update
// this.hdrsForCurrentSubBoard with the updated
// message header (for the message that was read)
if (retObj.savedVote)
{
if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(pMsgHdr.number))
{
var originalMsgIdx = this.hdrsForCurrentSubBoardByMsgNum[pMsgHdr.number];
if (typeof(this.msgbase.get_all_msg_headers) === "function")
var tmpHdrs = this.msgbase.get_all_msg_headers();
if (tmpHdrs.hasOwnProperty(pMsgHdr.number))
this.hdrsForCurrentSubBoard[originalMsgIdx] = tmpHdrs[pMsgHdr.number];
retObj.updatedHdr = pMsgHdr;
if (this.hdrsForCurrentSubBoard[originalMsgIdx].hasOwnProperty("total_votes"))
retObj.updatedHdr.total_votes = this.hdrsForCurrentSubBoard[originalMsgIdx].total_votes;
if (this.hdrsForCurrentSubBoard[originalMsgIdx].hasOwnProperty("upvotes"))
retObj.updatedHdr.upvotes = this.hdrsForCurrentSubBoard[originalMsgIdx].upvotes;
if (this.hdrsForCurrentSubBoard[originalMsgIdx].hasOwnProperty("tally"))
retObj.updatedHdr.tally = this.hdrsForCurrentSubBoard[originalMsgIdx].tally;
}
}
}
}
else
{
// Failed to save the vote
retObj.errorMsg = "Failed to save your vote";
}
}
return retObj;
}
// For the DigDistMsgReader class: Checks to see whether a user has voted on a message.
// The message must belong to the currently-open sub-board.
//
// Parameters:
// pMsgNum: The message number
// pUser: Optional - A user account to check. If omitted, the current logged-in
// user will be used.
function DigDistMsgReader_HasUserVotedOnMsg(pMsgNum, pUser)
{

nightfox
committed
// Don't do this for personal email
if (this.subBoardCode == "mail")
return false;
14170
14171
14172
14173
14174
14175
14176
14177
14178
14179
14180
14181
14182
14183
14184
14185
14186
// Thanks to echicken for explaining how to check this. To check a user's
// vote, use MsgBase.how_user_voted().
/*
The return value will be:
0 - user hasn't voted
1 - upvoted
2 - downvoted
Or, if the message was a poll, it's a bitfield:
if (votes&(1<<2)) {
// User selected answer 2
}
*/
var userHasVotedOnMsg = false;
if ((this.msgbase !== null) && this.msgbase.is_open)
{
if (typeof(this.msgbase.how_user_voted) === "function")
{
var votes = 0;
if (typeof(pUser) == "object")
votes = this.msgbase.how_user_voted(pMsgNum, (this.msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? pUser.name : pUser.alias);
else
votes = this.msgbase.how_user_voted(pMsgNum, (this.msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias);
userHasVotedOnMsg = (votes > 0);
}
}
return userHasVotedOnMsg;
}
// For the DigDistMsgReader class: Gets the body (text) of a message. If it's
// a poll, this method will format the message body with poll results. Otherwise,
// this method will simply get the message body.
//
// Parameters:
// pMsgHeader: The message header
//
// Return value: The poll results, colorized. If the message is not a
// poll message, then an empty string will be returned.
function DigDistMsgReader_GetMsgBody(pMsgHdr)
14208
14209
14210
14211
14212
14213
14214
14215
14216
14217
14218
14219
14220
14221
14222
14223
14224
14225
{
var msgBody = "";
if ((typeof(MSG_TYPE_POLL) != "undefined") && (pMsgHdr.type & MSG_TYPE_POLL) == MSG_TYPE_POLL)
{
// A poll is intended to be parsed (and displayed) using on the header data. The
// (optional) comments are stored in the hdr.field_list[] with type values of
// SMB_COMMENT (now defined in sbbsdefs.js) and the available answers are in the
// field_list[] with type values of SMB_POLL_ANSWER.
// The 'comments' and 'answers' are also a part of the message header, so you can
// grab them separately, then format and display them however you want. You can
// find them in the header.field_list array; each element in that array should be
// an object with a 'type' and a 'data' property. Relevant types here are
// SMB_COMMENT and SMB_POLL_ANSWER. (This is what I'm doing on the web, and I
// just ignore the message body for poll messages.)
if (pMsgHdr.hasOwnProperty("field_list"))
{
14226
14227
14228
14229
14230
14231
14232
14233
14234
14235
14236
14237
14238
14239
14240
14241
14242
14243
14244
14245
//var voteOptDescLen = 27;
// Figure out the longest length of the poll choices, with
// a maximum of 22 characters less than the terminal width.
// Use a minimum of 27 characters.
// That length will be used for the formatting strings for
// the poll results.
var voteOptDescLen = 0;
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
if (pMsgHdr.field_list[fieldI].type == SMB_POLL_ANSWER)
{
if (pMsgHdr.field_list[fieldI].data.length > voteOptDescLen)
voteOptDescLen = pMsgHdr.field_list[fieldI].data.length;
}
}
if (voteOptDescLen > console.screen_columns - 22)
voteOptDescLen = console.screen_columns - 22;
else if (voteOptDescLen < 27)
voteOptDescLen = 27;
// Format strings for outputting the voting option lines
var unvotedOptionFormatStr = "\1n\1c\1h%2d\1n\1c: \1w\1h%-" + voteOptDescLen + "s [%-4d %6.2f%]\1n";
var votedOptionFormatStr = "\1n\1c\1h%2d\1n\1c: \1" + "5\1w\1h%-" + voteOptDescLen + "s [%-4d %6.2f%]\1n";
// Add up the total number of votes so that we can
// calculate vote percentages.
var totalNumVotes = 0;
if (pMsgHdr.hasOwnProperty("tally"))
{
for (var tallyI = 0; tallyI < pMsgHdr.tally.length; ++tallyI)
totalNumVotes += pMsgHdr.tally[tallyI];
}
// Go through field_list and append the voting options and stats to
// msgBody
var pollComment = "";
var optionNum = 1;
var numVotes = 0;
var votePercentage = 0;
var tallyIdx = 0;
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
if (pMsgHdr.field_list[fieldI].type == SMB_COMMENT)
pollComment += pMsgHdr.field_list[fieldI].data + "\r\n";
else if (pMsgHdr.field_list[fieldI].type == SMB_POLL_ANSWER)
{
// Figure out the number of votes on this option and the
// vote percentage
if (pMsgHdr.hasOwnProperty("tally"))
{
if (tallyIdx < pMsgHdr.tally.length)
{
numVotes = pMsgHdr.tally[tallyIdx];
votePercentage = (numVotes / totalNumVotes) * 100;
}
}
// Append to the message text
msgBody += format(numVotes == 0 ? unvotedOptionFormatStr : votedOptionFormatStr,
optionNum++, pMsgHdr.field_list[fieldI].data.substr(0, voteOptDescLen),
numVotes, votePercentage);
if (numVotes > 0)
msgBody += " " + CHECK_CHAR;
msgBody += "\r\n";
++tallyIdx;
}
}
if (pollComment.length > 0)
msgBody = pollComment + "\r\n" + msgBody;

nightfox
committed
// If voting is allowed in this sub-board and the current logged-in
// user has not voted on this message, then append some text saying
// how to vote.
var votingAllowed = ((this.subBoardCode != "mail") && (((msg_area.sub[this.subBoardCode].settings & SUB_NOVOTING) == 0)));
if (votingAllowed && !this.HasUserVotedOnMsg(pMsgHdr.number))
{
msgBody += "\1n\r\n\1gTo vote in this poll, press \1w\1h"
+ this.enhReaderKeys.vote + "\1n\1g now.";
}
// If the current logged-in user created this poll, then show the
// users who have voted on it so far.
var msgFromUpper = pMsgHdr.from.toUpperCase();
if ((msgFromUpper == user.name.toUpperCase()) || (msgFromUpper == user.handle.toUpperCase()))
{
// Check all the messages in the messagebase after the current one
// to find poll response messages
if (typeof(this.msgbase.get_all_msg_headers) === "function")
{
// Get the line from text.dat for writing who voted & when. It
// is a format string and should look something like this:
//"\r\n\1n\1hOn %s, in \1c%s \1n\1c%s\r\n\1h\1m%s voted in your poll: \1n\1h%s\r\n" 787 PollVoteNotice
var userVotedInYourPollText = bbs.text(typeof(PollVoteNotice) != "undefined" ? PollVoteNotice : 787);
// Pass true to get_all_msg_headers() to tell it to return vote messages
// (the parameter was introduced in Synchronet 3.17+)
var tmpHdrs = this.msgbase.get_all_msg_headers(true);
for (var tmpProp in tmpHdrs)
{
if (tmpHdrs[tmpProp] == null)
continue;
// If this header's thread_back or reply_id matches the poll message
// number, then append the 'user voted' string to the message body.
if ((tmpHdrs[tmpProp].thread_back == pMsgHdr.number) || (tmpHdrs[tmpProp].reply_id == pMsgHdr.number))
{
var msgWrittenLocalTime = msgWrittenTimeToLocalBBSTime(tmpHdrs[tmpProp]);
var voteDate = strftime("%a %b %d %Y %H:%M:%S", msgWrittenLocalTime);
msgBody += format(userVotedInYourPollText, voteDate, this.msgbase.cfg.grp_name, this.msgbase.cfg.name,
tmpHdrs[tmpProp].from, pMsgHdr.subject);
}
}
}
}
}
}
else
msgBody = this.msgbase.get_msg_body(false, pMsgHdr.number);
return msgBody;
}
14343
14344
14345
14346
14347
14348
14349
14350
14351
14352
14353
14354
14355
14356
14357
14358
14359
14360
14361
14362
14363
14364
14365
14366
14367
14368
14369
14370
14371
14372
14373
14374
14375
14376
14377
14378
14379
14380
14381
14382
14383
14384
14385
14386
14387
14388
14389
14390
14391
14392
14393
14394
14395
14396
14397
14398
14399
14400
14401
14402
14403
14404
14405
14406
14407
14408
14409
14410
14411
14412
14413
14414
14415
14416
14417
14418
14419
14420
14421
14422
14423
14424
14425
14426
14427
14428
14429
14430
14431
14432
14433
14434
14435
14436
14437
14438
14439
14440
14441
14442
14443
14444
14445
14446
14447
14448
14449
14450
14451
14452
14453
14454
14455
14456
14457
14458
14459
14460
14461
14462
14463
14464
14465
14466
14467
14468
14469
///////////////////////////////////////////////////////////////////////////////////
// Helper functions
// Displays the program information.
function DisplayProgramInfo()
{
displayTextWithLineBelow("Digital Distortion Message Reader", true, "\1n\1c\1h", "\1k\1h")
console.center("\1n\1cVersion \1g" + READER_VERSION + " \1w\1h(\1b" + READER_DATE + "\1w)");
console.crlf();
}
// This function returns an array of default colors used in the
// DigDistMessageReader class.
function getDefaultColors()
{
var colorArray = new Array();
// Header line: "Current msg group:"
colorArray["msgListHeaderMsgGroupTextColor"] = "\1n\1" + "4\1c"; // Normal cyan on blue background
//colorArray["msgListHeaderMsgGroupTextColor"] = "\1n\1" + "4\1w"; // Normal white on blue background
// Header line: Message group name
colorArray["msgListHeaderMsgGroupNameColor"] = "\1h\1c"; // High cyan
//colorArray["msgListHeaderMsgGroupNameColor"] = "\1h\1w"; // High white
// Header line: "Current sub-board:"
colorArray["msgListHeaderSubBoardTextColor"] = "\1n\1" + "4\1c"; // Normal cyan on blue background
//colorArray["msgListHeaderSubBoardTextColor"] = "\1n\1" + "4\1w"; // Normal white on blue background
// Header line: Message sub-board name
colorArray["msgListHeaderMsgSubBoardName"] = "\1h\1c"; // High cyan
//colorArray["msgListHeaderMsgSubBoardName"] = "\1h\1w"; // High white
// Line with column headers
//colorArray["msgListColHeader"] = "\1h\1w"; // High white (keep blue background)
colorArray["msgListColHeader"] = "\1n\1h\1w"; // High white on black background
//colorArray["msgListColHeader"] = "\1h\1c"; // High cyan (keep blue background)
//colorArray["msgListColHeader"] = "\1" + "4\1h\1y"; // High yellow (keep blue background)
// Message list information
colorArray["msgListMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListFromColor"] = "\1n\1c";
colorArray["msgListToColor"] = "\1n\1c";
colorArray["msgListSubjectColor"] = "\1n\1c";
colorArray["msgListDateColor"] = "\1h\1b";
colorArray["msgListTimeColor"] = "\1h\1b";
// Message information for messages written to the user
colorArray["msgListToUserMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListToUserFromColor"] = "\1h\1g";
colorArray["msgListToUserToColor"] = "\1h\1g";
colorArray["msgListToUserSubjectColor"] = "\1h\1g";
colorArray["msgListToUserDateColor"] = "\1h\1b";
colorArray["msgListToUserTimeColor"] = "\1h\1b";
// Message information for messages from the user
colorArray["msgListFromUserMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListFromUserFromColor"] = "\1n\1c";
colorArray["msgListFromUserToColor"] = "\1n\1c";
colorArray["msgListFromUserSubjectColor"] = "\1n\1c";
colorArray["msgListFromUserDateColor"] = "\1h\1b";
colorArray["msgListFromUserTimeColor"] = "\1h\1b";
// Message list highlight colors
colorArray["msgListHighlightBkgColor"] = "\1" + "4"; // Background
colorArray["msgListMsgNumHighlightColor"] = "\1h\1y";
colorArray["msgListFromHighlightColor"] = "\1h\1c";
colorArray["msgListToHighlightColor"] = "\1h\1c";
colorArray["msgListSubjHighlightColor"] = "\1h\1c";
colorArray["msgListDateHighlightColor"] = "\1h\1w";
colorArray["msgListTimeHighlightColor"] = "\1h\1w";
// Lightbar message list help line colors
colorArray["lightbarMsgListHelpLineBkgColor"] = "\1" + "7"; // Background
colorArray["lightbarMsgListHelpLineGeneralColor"] = "\1b";
colorArray["lightbarMsgListHelpLineHotkeyColor"] = "\1r";
colorArray["lightbarMsgListHelpLineParenColor"] = "\1m";
// Continue prompt colors
colorArray["tradInterfaceContPromptMainColor"] = "\1n\1g"; // Main text color
colorArray["tradInterfaceContPromptHotkeyColor"] = "\1h\1c"; // Hotkey color
colorArray["tradInterfaceContPromptUserInputColor"] = "\1h\1g"; // User input color
// Message body color
colorArray["msgBodyColor"] = "\1n\1w";
// Read message confirmation colors
colorArray["readMsgConfirmColor"] = "\1n\1c";
colorArray["readMsgConfirmNumberColor"] = "\1h\1c";
// Prompt for continuing to list messages after reading a message
colorArray["afterReadMsg_ListMorePromptColor"] = "\1n\1c";
// Help screen text color
colorArray["tradInterfaceHelpScreenColor"] = "\1n\1h\1w";
// Colors for choosing a message group & sub-board
colorArray["areaChooserMsgAreaNumColor"] = "\1n\1w\1h";
colorArray["areaChooserMsgAreaDescColor"] = "\1n\1c";
colorArray["areaChooserMsgAreaNumItemsColor"] = "\1b\1h";
colorArray["areaChooserMsgAreaHeaderColor"] = "\1n\1y\1h";
colorArray["areaChooserSubBoardHeaderColor"] = "\1n\1g";
colorArray["areaChooserMsgAreaMarkColor"] = "\1g\1h";
colorArray["areaChooserMsgAreaLatestDateColor"] = "\1n\1g";
colorArray["areaChooserMsgAreaLatestTimeColor"] = "\1n\1m";
// Highlighted colors (for lightbar mode)
colorArray["areaChooserMsgAreaBkgHighlightColor"] = "\1" + "4"; // Blue background
colorArray["areaChooserMsgAreaNumHighlightColor"] = "\1w\1h";
colorArray["areaChooserMsgAreaDescHighlightColor"] = "\1c";
colorArray["areaChooserMsgAreaDateHighlightColor"] = "\1w\1h";
colorArray["areaChooserMsgAreaTimeHighlightColor"] = "\1w\1h";
colorArray["areaChooserMsgAreaNumItemsHighlightColor"] = "\1w\1h";
// Lightbar area chooser help line
colorArray["lightbarAreaChooserHelpLineBkgColor"] = "\1" + "7"; // Background
colorArray["lightbarAreaChooserHelpLineGeneralColor"] = "\1b";
colorArray["lightbarAreaChooserHelpLineHotkeyColor"] = "\1r";
colorArray["lightbarAreaChooserHelpLineParenColor"] = "\1m";
// Scrollbar background and scroll block colors (for the enhanced
// message reader interface)
colorArray["scrollbarBGColor"] = "\1n\1h\1k";
colorArray["scrollbarScrollBlockColor"] = "\1n\1h\1w";
// Color for the line drawn in the 2nd to last line of the message
// area in the enhanced reader mode before a prompt
colorArray["enhReaderPromptSepLineColor"] = "\1n\1h\1g";
// Colors for the enhanced reader help line
colorArray["enhReaderHelpLineBkgColor"] = "\1" + "7";
colorArray["enhReaderHelpLineGeneralColor"] = "\1b";
colorArray["enhReaderHelpLineHotkeyColor"] = "\1r";
colorArray["enhReaderHelpLineParenColor"] = "\1m";
// Message header line colors
colorArray["hdrLineLabelColor"] = "\1n\1c";
colorArray["hdrLineValueColor"] = "\1n\1b\1h";
// Selected message marker color
colorArray["selectedMsgMarkColor"] = "\1n\1w\1h";
14477
14478
14479
14480
14481
14482
14483
14484
14485
14486
14487
14488
14489
14490
14491
14492
14493
14494
14495
14496
14497
14498
14499
14500
14501
14502
14503
14504
14505
14506
14507
14508
14509
14510
14511
14512
14513
14514
14515
14516
14517
14518
14519
14520
14521
14522
14523
14524
14525
14526
14527
14528
14529
14530
14531
14532
14533
14534
14535
14536
14537
14538
14539
14540
14541
14542
14543
14544
14545
14546
14547
14548
14549
14550
14551
14552
14553
14554
14555
14556
14557
14558
14559
14560
14561
14562
14563
14564
14565
14566
return colorArray;
}
// This function returns the month number (1-based) from a capitalized
// month name.
//
// Parameters:
// pMonthName: The name of the month
//
// Return value: The number of the month (1-12).
function getMonthNum(pMonthName)
{
var monthNum = 1;
if (pMonthName.substr(0, 3) == "Jan")
monthNum = 1;
else if (pMonthName.substr(0, 3) == "Feb")
monthNum = 2;
else if (pMonthName.substr(0, 3) == "Mar")
monthNum = 3;
else if (pMonthName.substr(0, 3) == "Apr")
monthNum = 4;
else if (pMonthName.substr(0, 3) == "May")
monthNum = 5;
else if (pMonthName.substr(0, 3) == "Jun")
monthNum = 6;
else if (pMonthName.substr(0, 3) == "Jul")
monthNum = 7;
else if (pMonthName.substr(0, 3) == "Aug")
monthNum = 8;
else if (pMonthName.substr(0, 3) == "Sep")
monthNum = 9;
else if (pMonthName.substr(0, 3) == "Oct")
monthNum = 10;
else if (pMonthName.substr(0, 3) == "Nov")
monthNum = 11;
else if (pMonthName.substr(0, 3) == "Dec")
monthNum = 12;
return monthNum;
}
// Clears each line from a given line to the end of the screen.
//
// Parameters:
// pStartLineNum: The line number to start at (1-based)
function clearToEOS(pStartLineNum)
{
if (typeof(pStartLineNum) == "undefined")
return;
if (pStartLineNum == null)
return;
for (var lineNum = pStartLineNum; lineNum <= console.screen_rows; ++lineNum)
{
console.gotoxy(1, lineNum);
console.clearline();
}
}
// Returns the number of messages in a sub-board.
//
// Parameters:
// pSubBoardCode: The sub-board code (i.e., from bbs.cursub_code)
//
// Return value: The number of messages in the sub-board, or 0
// if the sub-board could not be opened.
function numMessages(pSubBoardCode)
{
var messageCount = 0;
var myMsgbase = new MsgBase(pSubBoardCode);
if (myMsgbase.open())
messageCount = myMsgbase.total_msgs;
myMsgbase.close();
myMsgbase = null;
return messageCount;
}
// Removes multiple, leading, and/or trailing spaces
// The search & replace regular expressions used in this
// function came from the following URL:
// http://qodo.co.uk/blog/javascript-trim-leading-and-trailing-spaces
//
// Parameters:
// pString: The string to trim
// pLeading: Whether or not to trim leading spaces (optional, defaults to true)
// pMultiple: Whether or not to trim multiple spaces (optional, defaults to true)
// pTrailing: Whether or not to trim trailing spaces (optional, defaults to true)

nightfox
committed
//
// Return value: The string with whitespace trimmed
14569
14570
14571
14572
14573
14574
14575
14576
14577
14578
14579
14580
14581
14582
14583
14584
14585
14586
14587
14588
14589
14590
14591
14592
14593
14594
14595
14596
14597
14598
14599
14600
14601
14602
14603
14604
14605
14606
14607
14608
14609
14610
14611
14612
14613
14614
14615
14616
14617
14618
function trimSpaces(pString, pLeading, pMultiple, pTrailing)
{
var leading = true;
var multiple = true;
var trailing = true;
if(typeof(pLeading) != "undefined")
leading = pLeading;
if(typeof(pMultiple) != "undefined")
multiple = pMultiple;
if(typeof(pTrailing) != "undefined")
trailing = pTrailing;
// To remove both leading & trailing spaces:
//pString = pString.replace(/(^\s*)|(\s*$)/gi,"");
if (leading)
pString = pString.replace(/(^\s*)/gi,"");
if (multiple)
pString = pString.replace(/[ ]{2,}/gi," ");
if (trailing)
pString = pString.replace(/(\s*$)/gi,"");
return pString;
}
// Returns whether an internal sub-board code is valid.
//
// Parameters:
// pSubBoardCode: The internal sub-board code to test
//
// Return value: Boolean - Whether or not the given internal code is a valid
// sub-board code
function subBoardCodeIsValid(pSubBoardCode)
{
return ((pSubBoardCode == "mail") || (typeof(msg_area.sub[pSubBoardCode]) == "object"))
}
// Displays some text with a solid horizontal line on the next line.
//
// Parameters:
// pText: The text to display
// pCenter: Whether or not to center the text. Optional; defaults
// to false.
// pTextColor: The color to use for the text. Optional; by default,
// normal white will be used.
// pLineColor: The color to use for the line underneath the text.
// Optional; by default, bright black will be used.
function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor)
{
var centerText = (typeof(pCenter) == "boolean" ? pCenter : false);
var textColor = (typeof(pTextColor) == "string" ? pTextColor : "\1n\1w");
var lineColor = (typeof(pLineColor) == "string" ? pLineColor : "\1n\1k\1h");
14622
14623
14624
14625
14626
14627
14628
14629
14630
14631
14632
14633
14634
14635
14636
14637
14638
14639
14640
14641
// Output the text and a solid line on the next line.
if (centerText)
{
console.center(textColor + pText);
var solidLine = "";
var textLength = console.strlen(pText);
for (var i = 0; i < textLength; ++i)
solidLine += "Ä";
console.center(lineColor + solidLine);
}
else
{
console.print(textColor + pText);
console.crlf();
console.print(lineColor);
var textLength = console.strlen(pText);
for (var i = 0; i < textLength; ++i)
console.print("Ä");
console.crlf();
}
14642
14643
14644
14645
14646
14647
14648
14649
14650
14651
14652
14653
14654
14655
14656
14657
14658
14659
14660
14661
14662
14663
14664
14665
14666
14667
14668
14669
14670
14671
14672
14673
14674
14675
14676
14677
14678
14679
14680
14681
14682
14683
14684
14685
14686
14687
14688
14689
14690
14691
14692
14693
14694
14695
14696
14697
14698
14699
14700
14701
14702
14703
14704
14705
14706
14707
14708
14709
14710
14711
14712
14713
14714
14715
14716
14717
14718
14719
14720
14721
14722
14723
14724
14725
14726
14727
14728
14729
14730
14731
14732
14733
14734
14735
14736
14737
14738
14739
14740
14741
14742
14743
14744
14745
14746
14747
14748
14749
14750
14751
14752
14753
14754
14755
14756
14757
14758
14759
14760
14761
14762
14763
14764
14765
14766
14767
14768
14769
14770
14771
14772
14773
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
14786
14787
14788
14789
14790
14791
14792
14793
14794
14795
14796
14797
14798
14799
14800
14801
14802
14803
14804
14805
14806
14807
14808
14809
14810
14811
14812
14813
14814
14815
14816
14817
14818
14819
14820
14821
14822
14823
14824
14825
14826
14827
14828
14829
14830
14831
14832
14833
14834
14835
14836
14837
14838
14839
14840
14841
14842
14843
14844
14845
14846
14847
14848
14849
14850
14851
14852
14853
14854
14855
14856
14857
14858
14859
14860
14861
14862
14863
14864
14865
14866
14867
14868
14869
14870
14871
14872
14873
14874
14875
14876
14877
14878
14879
14880
14881
14882
14883
14884
14885
14886
14887
14888
14889
14890
14891
14892
14893
14894
14895
14896
14897
14898
14899
14900
14901
14902
14903
14904
14905
14906
14907
14908
14909
14910
14911
14912
14913
14914
14915
14916
14917
14918
14919
14920
14921
14922
14923
14924
14925
14926
14927
14928
14929
14930
14931
14932
14933
14934
14935
14936
14937
14938
14939
14940
14941
14942
14943
14944
14945
14946
14947
14948
14949
14950
14951
14952
14953
14954
14955
14956
14957
14958
14959
14960
14961
14962
14963
14964
14965
14966
14967
14968
14969
14970
14971
14972
14973
14974
14975
14976
14977
14978
14979
14980
14981
14982
14983
14984
14985
14986
14987
14988
14989
14990
14991
14992
14993
14994
14995
14996
14997
14998
14999
15000
}
// Returns whether the Synchronet compile date is at least May 12, 2013. That
// was when Digital Man's change to make bbs.msg_number work when a script is
// running first went into the Synchronet daily builds.
function compileDateAtLeast2013_05_12()
{
// system.compiled_when is in the following format:
// May 12 2013 05:02
var compileDateParts = system.compiled_when.split(" ");
if (compileDateParts.length < 4)
return false;
// Convert the month to a 1-based number
var compileMonth = 0;
if (/^Jan/.test(compileDateParts[0]))
compileMonth = 1;
else if (/^Feb/.test(compileDateParts[0]))
compileMonth = 2;
else if (/^Mar/.test(compileDateParts[0]))
compileMonth = 3;
else if (/^Apr/.test(compileDateParts[0]))
compileMonth = 4;
else if (/^May/.test(compileDateParts[0]))
compileMonth = 5;
else if (/^Jun/.test(compileDateParts[0]))
compileMonth = 6;
else if (/^Jul/.test(compileDateParts[0]))
compileMonth = 7;
else if (/^Aug/.test(compileDateParts[0]))
compileMonth = 8;
else if (/^Sep/.test(compileDateParts[0]))
compileMonth = 9;
else if (/^Oct/.test(compileDateParts[0]))
compileMonth = 10;
else if (/^Nov/.test(compileDateParts[0]))
compileMonth = 11;
else if (/^Dec/.test(compileDateParts[0]))
compileMonth = 12;
// Get the compileDay and compileYear as numeric variables
var compileDay = +compileDateParts[1];
var compileYear = +compileDateParts[2];
// Determine if the compile date is at least 2013-05-12
var compileDateIsAtLeastMin = true;
if (compileYear > 2013)
compileDateIsAtLeastMin = true;
else if (compileYear < 2013)
compileDateIsAtLeastMin = false;
else // compileYear is 2013
{
if (compileMonth > 5)
compileDateIsAtLeastMin = true
else if (compileMonth < 5)
compileDateIsAtLeastMin = false;
else // compileMonth is 5
compileDateIsAtLeastMin = (compileDay >= 12);
}
return compileDateIsAtLeastMin;
}
// Removes multiple, leading, and/or trailing spaces.
// The search & replace regular expressions used in this
// function came from the following URL:
// http://qodo.co.uk/blog/javascript-trim-leading-and-trailing-spaces
//
// Parameters:
// pString: The string to trim
// pLeading: Whether or not to trim leading spaces (optional, defaults to true)
// pMultiple: Whether or not to trim multiple spaces (optional, defaults to true)
// pTrailing: Whether or not to trim trailing spaces (optional, defaults to true)
function trimSpaces(pString, pLeading, pMultiple, pTrailing)
{
var leading = true;
var multiple = true;
var trailing = true;
if(typeof(pLeading) != "undefined")
leading = pLeading;
if(typeof(pMultiple) != "undefined")
multiple = pMultiple;
if(typeof(pTrailing) != "undefined")
trailing = pTrailing;
// To remove both leading & trailing spaces:
//pString = pString.replace(/(^\s*)|(\s*$)/gi,"");
if (leading)
pString = pString.replace(/(^\s*)/gi,"");
if (multiple)
pString = pString.replace(/[ ]{2,}/gi," ");
if (trailing)
pString = pString.replace(/(\s*$)/gi,"");
return pString;
}
// Calculates & returns a page number.
//
// Parameters:
// pTopIndex: The index (0-based) of the topmost item on the page
// pNumPerPage: The number of items per page
//
// Return value: The page number
function calcPageNum(pTopIndex, pNumPerPage)
{
return ((pTopIndex / pNumPerPage) + 1);
}
// Returns the greatest number of messages of all sub-boards within
// a message group.
//
// Parameters:
// pGrpIndex: The index of the message group
//
// Returns: The greatest number of messages of all sub-boards within
// the message group
function getGreatestNumMsgs(pGrpIndex)
{
// Sanity checking
if (typeof(pGrpIndex) != "number")
return 0;
if (typeof(msg_area.grp_list[pGrpIndex]) == "undefined")
return 0;
var greatestNumMsgs = 0;
var msgBase = null;
for (var subIndex = 0; subIndex < msg_area.grp_list[pGrpIndex].sub_list.length; ++subIndex)
{
msgBase = new MsgBase(msg_area.grp_list[pGrpIndex].sub_list[subIndex].code);
if (msgBase == null) continue;
if (msgBase.open())
{
if (msgBase.total_msgs > greatestNumMsgs)
greatestNumMsgs = msgBase.total_msgs;
msgBase.close();
}
}
return greatestNumMsgs;
}
// Inputs a keypress from the user and handles some ESC-based
// characters such as PageUp, PageDown, and ESC. If PageUp
// or PageDown are pressed, this function will return the
// string "\1PgUp" (KEY_PAGE_UP) or "\1Pgdn" (KEY_PAGE_DOWN),
// respectively. Also, F1-F5 will be returned as "\1F1"
// through "\1F5", respectively.
// Thanks goes to Psi-Jack for the original impementation
// of this function.
//
// Parameters:
// pGetKeyMode: Optional - The mode bits for console.getkey().
// If not specified, K_NONE will be used.
//
// Return value: The user's keypress
function getKeyWithESCChars(pGetKeyMode)
{
var getKeyMode = K_NONE;
if (typeof(pGetKeyMode) == "number")
getKeyMode = pGetKeyMode;
var userInput = console.getkey(getKeyMode);
if (userInput == KEY_ESC) {
switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
case '[':
switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
case 'V':
userInput = KEY_PAGE_UP;
break;
case 'U':
userInput = KEY_PAGE_DOWN;
break;
}
break;
case 'O':
switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
case 'P':
userInput = "\1F1";
break;
case 'Q':
userInput = "\1F2";
break;
case 'R':
userInput = "\1F3";
break;
case 'S':
userInput = "\1F4";
break;
case 't':
userInput = "\1F5";
break;
}
default:
break;
}
}
return userInput;
}
// Finds the next or previous non-empty message sub-board. Returns an
// object containing the message group & sub-board indexes. If all of
// the next/previous sub-boards are empty, then the given current indexes
// will be returned.
//
// Parameters:
// pStartGrpIdx: The index of the message group to start from
// pStartSubIdx: The index of the sub-board in the message group to start from
// pForward: Boolean - Whether or not to search forward (true) or backward (false).
// Optional; defaults to true, to search forward.
//
// Return value: An object with the following properties:
// foundSubBoard: Boolean - Whether or not a different sub-board was found
// grpIdx: The message group index of the found sub-board
// subIdx: The sub-board index in the group of the found sub-board
// subCode: The internal code of the sub-board
// subChanged: Boolean - Whether or not the found sub-board is
// different from the one that was passed in
// paramsValid: Boolean - Whether or not all the passed-in parameters
// were valid.
function findNextOrPrevNonEmptySubBoard(pStartGrpIdx, pStartSubIdx, pForward)
{
var retObj = new Object();
retObj.grpIdx = pStartGrpIdx;
retObj.subIdx = pStartSubIdx;
retObj.subCode = msg_area.grp_list[pStartGrpIdx].sub_list[pStartSubIdx].code;
retObj.foundSubBoard = false;
// Sanity checking
retObj.paramsValid = ((pStartGrpIdx >= 0) && (pStartGrpIdx < msg_area.grp_list.length) &&
(pStartSubIdx >= 0) &&
(pStartSubIdx < msg_area.grp_list[pStartGrpIdx].sub_list.length));
if (!retObj.paramsValid)
return retObj;
var grpIdx = pStartGrpIdx;
var subIdx = pStartSubIdx;
var searchForward = (typeof(pForward) == "boolean" ? pForward : true);
if (searchForward)
{
// Advance the sub-board (and group) index, and determine whether or not
// to do the search (i.e., we might not want to if the starting sub-board
// is the last sub-board in the last group).
var searchForSubBoard = true;
if (subIdx < msg_area.grp_list[grpIdx].sub_list.length - 1)
++subIdx;
else
{
if ((grpIdx < msg_area.grp_list.length - 1) && (msg_area.grp_list[grpIdx+1].sub_list.length > 0))
{
subIdx = 0;
++grpIdx;
}
else
searchForSubBoard = false;
}
// If we can search, then do it.
if (searchForSubBoard)
{
while (numMsgsInSubBoard(msg_area.grp_list[grpIdx].sub_list[subIdx].code) == 0)
{
if (subIdx < msg_area.grp_list[grpIdx].sub_list.length - 1)
++subIdx;
else
{
if ((grpIdx < msg_area.grp_list.length - 1) && (msg_area.grp_list[grpIdx+1].sub_list.length > 0))
{
subIdx = 0;
++grpIdx;
}
else
break; // Stop searching
}
}
}
}
else
{
// Search the sub-boards in reverse
// Decrement the sub-board (and group) index, and determine whether or not
// to do the search (i.e., we might not want to if the starting sub-board
// is the first sub-board in the first group).
var searchForSubBoard = true;
if (subIdx > 0)
--subIdx;
else
{
if ((grpIdx > 0) && (msg_area.grp_list[grpIdx-1].sub_list.length > 0))
{
--grpIdx;
subIdx = msg_area.grp_list[grpIdx].sub_list.length - 1;
}
else
searchForSubBoard = false;
}
// If we can search, then do it.
if (searchForSubBoard)
{
while (numMsgsInSubBoard(msg_area.grp_list[grpIdx].sub_list[subIdx].code) == 0)
{
if (subIdx > 0)
--subIdx;
else
{
if ((grpIdx > 0) && (msg_area.grp_list[grpIdx-1].sub_list.length > 0))
{
--grpIdx;
subIdx = msg_area.grp_list[grpIdx].sub_list.length - 1;
}
else
break; // Stop searching
}
}
}
}
// If we found a sub-board with messages in it, then set the variables
// in the return object
if (numMsgsInSubBoard(msg_area.grp_list[grpIdx].sub_list[subIdx].code) > 0)
{
retObj.grpIdx = grpIdx;
retObj.subIdx = subIdx;
retObj.subCode = msg_area.grp_list[grpIdx].sub_list[subIdx].code;
retObj.foundSubBoard = true;
retObj.subChanged = ((grpIdx != pStartGrpIdx) || (subIdx != pStartSubIdx));
}
return retObj;
}
// Returns the number of messages in a sub-board.
//
// Parameters:
// pSubBoardCode: The internal code of the sub-board to check
// pIncludeDeleted: Optional boolean - Whether or not to include deleted
// messages in the count. Defaults to false.
//
// Return value: The number of messages in the sub-board
function numMsgsInSubBoard(pSubBoardCode, pIncludeDeleted)
{
var numMessages = 0;
var msgbase = new MsgBase(pSubBoardCode);
if (msgbase.open())
{
var includeDeleted = (typeof(pIncludeDeleted) == "boolean" ? pIncludeDeleted : false);
if (includeDeleted)
numMessages = msgbase.total_msgs;
else
{
// Don't include deleted messages. Go through each message
// in the sub-board and count the ones that aren't marked
// as deleted.
for (var msgIdx = 0; msgIdx < msgbase.total_msgs; ++msgIdx)
{
var msgHdr = msgbase.get_msg_header(true, msgIdx, false);
if ((msgHdr != null) && ((msgHdr.attr & MSG_DELETE) == 0))
++numMessages;
}