Newer
Older
// answers/votes per ballot (0 is the equivalent of 1).
// Max votes testing? :
// userVotedMaxVotes = (votes == pMsgHdr.votes);
14004
14005
14006
14007
14008
14009
14010
14011
14012
14013
14014
14015
14016
14017
14018
14019
14020
14021
14022
14023
14024
14025
if (votes >= 0)
{
if ((votes == 0) || (votes == 1))
userVotedMaxVotes = (votes == 3); // (1 << 0) | (1 << 1);
else
{
userVotedMaxVotes = true;
for (var voteIdx = 0; voteIdx <= numVotes; ++voteIdx)
{
if (votes && (1 << voteIdx) == 0)
{
userVotedMaxVotes = false;
break;
}
}
}
}
}
var pollIsClosed = ((pMsgHdr.auxattr & POLL_CLOSED) == POLL_CLOSED);
if (pollIsClosed)
{
retObj.errorMsg = "This poll is closed";
msgbase.close();
return retObj;
}
else if (userVotedMaxVotes)
{
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;
msgbase.close();
return retObj;
}
}
// If the user has voted on this message already, then set an error message and return.
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;
msgbase.close();
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
// 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 = (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias;
14065
14066
14067
14068
14069
14070
14071
14072
14073
14074
14075
14076
14077
14078
14079
14080
14081
14082
14083
14084
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 & process the selection from the user
var userInputNum = 0;
if (pMsgHdr.votes > 1)
{
// Support multiple answers from the user
console.print("\1n\1gYour vote numbers, separated by commas, up to \1h" + pMsgHdr.votes + "\1n\1g (Q=quit):");
console.crlf();
console.print("\1c\1h");
//consoleGetStrWithValidKeys(pKeys, pMaxNumChars, pCaseSensitive)
var userInput = consoleGetStrWithValidKeys("0123456789,");
14102
14103
14104
14105
14106
14107
14108
14109
14110
14111
14112
14113
14114
14115
14116
14117
14118
14119
14120
14121
14122
14123
14124
14125
14126
14127
14128
14129
14130
14131
14132
14133
14134
14135
14136
14137
14138
14139
14140
14141
if ((userInput.length > 0) && (userInput.toUpperCase() != "Q"))
{
var userAnswers = userInput.split(",");
if (userAnswers.length > 0)
{
// Generate confirmation text and an array of numbers
// representing the user's choices, up to the number
// of responses allowed
var confirmText = "Vote ";
var voteNumbers = [];
for (var i = 0; (i < userAnswers.length) && (i < pMsgHdr.votes); ++i)
{
// Trim any whitespace from the user's response
userAnswers[i] = trimSpaces(userAnswers[i], true, true, true);
if (/^[0-9]+$/.test(userAnswers[i]))
{
voteNumbers.push(+userAnswers[i]);
confirmText += userAnswers[i] + ",";
}
}
// If the confirmation text has a trailing comma, remove it
if (/,$/.test(confirmText))
confirmText = confirmText.substr(0, confirmText.length-1);
// Confirm from the user and submit their vote if they say yes
if (voteNumbers.length > 0)
{
if (console.yesno(confirmText))
{
userInputNum = 0;
for (var i = 0; i < voteNumbers.length; ++i)
userInputNum |= (1 << (voteNumbers[i]-1));
}
else
retObj.userQuit = true;
}
}
}
else
retObj.userQuit = true;
}
else
{
// Get the selection prompt text from text.dat and replace the %u or %d with
// the number 1 (default option)
var selectPromptText = bbs.text(SelectItemWhich);
selectPromptText = selectPromptText.replace(/%[uU]/, 1).replace(/%[dD]/, 1);
console.mnemonics(selectPromptText);
var maxNum = optionNum - 1;
userInputNum = console.getnum(maxNum);
if (userInputNum == -1) // The user chose Q to quit
retObj.userQuit = true;
console.print("\1n");
}
if (!retObj.userQuit)
{
voteMsgHdr.attr = MSG_VOTE;
voteMsgHdr.votes = userInputNum;
}
}
}
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;
}
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 = 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(msgbase.get_all_msg_headers) === "function")
var tmpHdrs = 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";
}
msgbase.close();
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;
// 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;
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
if (typeof(msgbase.how_user_voted) === "function")
{
var votes = 0;
if (typeof(pUser) == "object")
votes = msgbase.how_user_voted(pMsgNum, (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? pUser.name : pUser.alias);
else
votes = msgbase.how_user_voted(pMsgNum, (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias);
userHasVotedOnMsg = (votes > 0);
}
msgbase.close();
}
return userHasVotedOnMsg;
}

nightfox
committed
14286
14287
14288
14289
14290
14291
14292
14293
14294
14295
14296
14297
14298
14299
14300
14301
14302
14303
14304
14305
14306
14307
14308
14309
14310
14311
14312
14313
14314
14315
// Gets information about the upvotes and downvotes for a message.
// If the user is a sysop, this will also get who voted on the message.
//
// Parameters:
// pMsgHdr: A header of a message that has upvotes & downvotes
//
// Return value: An array of strings containing information about the upvotes,
// downvotes, tally, and (if the user is a sysop) who submitted
// votes on the message.
function DigDistMsgReader_GetUpvoteAndDownvoteInfo(pMsgHdr)
{
// If the message header doesn't have the "total_votes" or "upvotes" properties,
// then there's no vote information, so just return an empty array.
if (!pMsgHdr.hasOwnProperty("total_votes") || !pMsgHdr.hasOwnProperty("upvotes"))
return [];
var msgUpvotes = pMsgHdr.upvotes;
var msgDownvotes = pMsgHdr.total_votes - pMsgHdr.upvotes;
var msgVoteScore = pMsgHdr.upvotes - msgDownvotes;
var voteInfo = [];
voteInfo.push("Upvotes: " + msgUpvotes);
voteInfo.push("Downvotes: " + msgDownvotes);
voteInfo.push("Score: " + msgVoteScore);
if (pMsgHdr.hasOwnProperty("tally"))
voteInfo.push("Tally: " + pMsgHdr.tally);
if (gIsSysop)
{
// Check all the messages in the messagebase after the current one
// to find response messages
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())

nightfox
committed
{
if (typeof(msgbase.get_all_msg_headers) === "function")

nightfox
committed
{
// Pass true to get_all_msg_headers() to tell it to return vote messages
// (the parameter was introduced in Synchronet 3.17+)
var tmpHdrs = msgbase.get_all_msg_headers(true);
for (var tmpProp in tmpHdrs)

nightfox
committed
{
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))

nightfox
committed
{
var tmpMessageBody = msgbase.get_msg_body(false, tmpHdrs[tmpProp].number);
if ((tmpHdrs[tmpProp].field_list.length == 0) && (tmpMessageBody.length == 0))
{
var msgWrittenLocalTime = msgWrittenTimeToLocalBBSTime(tmpHdrs[tmpProp]);
var voteDate = strftime("%a %b %d %Y %H:%M:%S", msgWrittenLocalTime);
voteInfo.push("\1n\1c\1h" + tmpHdrs[tmpProp].from + "\1n\1c voted on this message on " + voteDate + "\1n");
}

nightfox
committed
}
}
}
msgbase.close();

nightfox
committed
}
}
return voteInfo;
}
// 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)
{
var msgbase = new MsgBase(this.subBoardCode);
if (!msgbase.open())
return "";
14363
14364
14365
14366
14367
14368
14369
14370
14371
14372
14373
14374
14375
14376
14377
14378
14379
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"))
{
14380
14381
14382
14383
14384
14385
14386
14387
14388
14389
14390
14391
14392
14393
14394
14395
14396
14397
14398
// 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))

nightfox
committed
msgBody += "\1n\r\n\1gTo vote in this poll, press \1w\1h" + this.enhReaderKeys.vote + "\1n\1g now.\r\n";
// 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(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 = 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);
var grpName = "";
var msgbaseCfgName = "";
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
grpName = msgbase.cfg.grp_name;
msgbaseCfgName = msgbase.cfg.name;
msgbase.close();
}
msgBody += format(userVotedInYourPollText, voteDate, grpName, msgbaseCfgName, tmpHdrs[tmpProp].from, pMsgHdr.subject);
}
}
}
}
}
}
else
msgBody = msgbase.get_msg_body(false, pMsgHdr.number);
msgbase.close();
// Remove any Synchronet pause codes that might exist in the message
msgBody = msgBody.replace("\1p", "").replace("\1P", "");
// If the user is a sysop, this is a moderated message area, and the message
// hasn't been validated, then prepend the message with a message to let the
// sysop now know to validate it.
if (this.subBoardCode != "mail")
{
if (gIsSysop && msg_area.sub[this.subBoardCode].is_moderated && ((pMsgHdr.attr & MSG_VALIDATED) == 0))
{
var validateNotice = "\1n\1h\1yThis is an unvalidated message in a moderated area. Press "
+ this.enhReaderKeys.validateMsg + " to validate it.\r\n\1g";
for (var i = 0; i < 79; ++i)
validateNotice += HORIZONTAL_SINGLE;
validateNotice += "\1n\r\n";
msgBody = validateNotice + msgBody;
}
}
return msgBody;
}
// For the DigDistMsgReader class: Refreshes a message header in one of the
// internal message arrays.
//
// Parameters:
// pMsgNum: The number of the message to replace
function DigDistMsgReader_RefreshMsgHdrInArrays(pMsgNum)
{
var msgbase = new MsgBase(this.subBoardCode);
if (!msgbase.open())
return;
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 == pMsgNum)
{
var newMsgHdr = msgbase.get_msg_header(false, pMsgNum, true);
if (newMsgHdr != null)
this.msgSearchHdrs[this.subBoardCode].indexed[i] = newMsgHdr;
break;
}
}
}
else if (this.hdrsForCurrentSubBoard.length > 0)
{
if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(pMsgNum))
{
var msgHdrs = msgbase.get_all_msg_headers();
if (msgHdrs.hasOwnProperty(pMsgNum))
{
var msgIdx = this.hdrsForCurrentSubBoardByMsgNum[pMsgNum];
this.hdrsForCurrentSubBoard[msgIdx] = msgHdrs[pMsgNum];
}
}
}
14558
14559
14560
14561
14562
14563
14564
14565
14566
14567
14568
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
14619
14620
14621
14622
14623
14624
14625
msgbase.close();
}
// For the DigDistMessageReader class: Re-calculates the message list widths and
// format strings
//
// Parameters:
// pMsgNumLen: Optional - Length to use for the message number field. If not specified,
// then this will get the number of messages in the sub-board and use that
// length.
function DigDistMsgReader_RecalcMsgListWidthsAndFormatStrs(pMsgNumLen)
{
this.MSGNUM_LEN = (typeof(pMsgNumLen) == "number" ? pMsgNumLen : this.NumMessages(null, true).toString().length);
if (this.MSGNUM_LEN < 4)
this.MSGNUM_LEN = 4;
this.DATE_LEN = 10; // i.e., YYYY-MM-DD
this.TIME_LEN = 8; // i.e., HH:MM:SS
// Variable field widths: From, to, and subject
this.FROM_LEN = (console.screen_columns * (15/console.screen_columns)).toFixed(0);
this.TO_LEN = (console.screen_columns * (15/console.screen_columns)).toFixed(0);
var colsLeftForSubject = console.screen_columns-this.MSGNUM_LEN-this.DATE_LEN-this.TIME_LEN-this.FROM_LEN-this.TO_LEN-6; // 6 to account for the spaces
this.SUBJ_LEN = (console.screen_columns * (colsLeftForSubject/console.screen_columns)).toFixed(0);
// Construct the message list header format string
this.sHdrFormatStr = "%" + this.MSGNUM_LEN + "s %-" + this.FROM_LEN + "s %-"
+ this.TO_LEN + "s %-" + this.SUBJ_LEN + "s %-"
+ this.DATE_LEN + "s %-" + this.TIME_LEN + "s";
// If the user's terminal doesn't support ANSI, then append a newline to
// the end of the format string (we won't be able to move the cursor).
if (!canDoHighASCIIAndANSI())
this.sHdrFormatStr += "\r\n";
// Construct the message information format string. These must be done after
// reading the configuration file, because the configuration file specifies the
// colors to use.
this.sMsgInfoFormatStr = this.colors.msgListMsgNumColor + "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListFromColor + "%-" + this.FROM_LEN + "s "
+ this.colors.msgListToColor + "%-" + this.TO_LEN + "s "
+ this.colors.msgListSubjectColor + "%-" + this.SUBJ_LEN + "s "
+ this.colors.msgListDateColor + "%-" + this.DATE_LEN + "s "
+ this.colors.msgListTimeColor + "%-" + this.TIME_LEN + "s";
// Message information format string with colors to use when the message is
// written to the user.
this.sMsgInfoToUserFormatStr = this.colors.msgListToUserMsgNumColor + "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListToUserFromColor
+ "%-" + this.FROM_LEN + "s " + this.colors.msgListToUserToColor + "%-"
+ this.TO_LEN + "s " + this.colors.msgListToUserSubjectColor + "%-"
+ this.SUBJ_LEN + "s " + this.colors.msgListToUserDateColor
+ "%-" + this.DATE_LEN + "s " + this.colors.msgListToUserTimeColor
+ "%-" + this.TIME_LEN + "s";
// Message information format string with colors to use when the message is
// from the user.
this.sMsgInfoFromUserFormatStr = this.colors.msgListFromUserMsgNumColor + "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListFromUserFromColor
+ "%-" + this.FROM_LEN + "s " + this.colors.msgListFromUserToColor + "%-"
+ this.TO_LEN + "s " + this.colors.msgListFromUserSubjectColor + "%-"
+ this.SUBJ_LEN + "s " + this.colors.msgListFromUserDateColor
+ "%-" + this.DATE_LEN + "s " + this.colors.msgListFromUserTimeColor
+ "%-" + this.TIME_LEN + "s";
// Highlighted message information line for the message list (used for the
// lightbar interface)
this.sMsgInfoFormatHighlightStr = this.colors.msgListMsgNumHighlightColor
+ "%" + this.MSGNUM_LEN + "d%s"
+ this.colors.msgListFromHighlightColor + "%-" + this.FROM_LEN
+ "s " + this.colors.msgListToHighlightColor + "%-" + this.TO_LEN + "s "
+ this.colors.msgListSubjHighlightColor + "%-" + this.SUBJ_LEN + "s "
+ this.colors.msgListDateHighlightColor + "%-" + this.DATE_LEN + "s "
+ this.colors.msgListTimeHighlightColor + "%-" + this.TIME_LEN + "s";
}
14628
14629
14630
14631
14632
14633
14634
14635
14636
14637
14638
14639
14640
14641
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
///////////////////////////////////////////////////////////////////////////////////
// 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";
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
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
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
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");
14907
14908
14909
14910
14911
14912
14913
14914
14915
14916
14917
14918
14919
14920
14921
14922
14923
14924
14925
14926
// 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();
}
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)