1var appPrefs = { 2 savedTextArea: "", 3 flPostOnReturnKey: false, 4 flEchoPostsToTwitter: false, 5 hashTagForTwitter: "", flSendHashTagToTwitter: true 6 }; 7 8function chatApp (options, callback) { 9 var chatOptions = { 10 flReadOnlyMode: false, 11 flAutoSave: true, minSecsBetwAutoSaves: 1, 12 urlChatServer: undefined, 13 urlChatSocket: undefined, 14 editorPlaceholderText: "", 15 userInfoFromTwitter: undefined, //this should come from the identity system 16 flEveryoneFollowsEnabled: false, //5/8/19 by DW -- turn the everyoneFollows feature on by setting this true 17 everyoneFollows: {"davewiner": true}, //8/23/17 by DW -- screennames of users everyone follows 18 defaultEditorButtons: ["bold", "italic", "anchor", "h4", "orderedlist", "unorderedlist", "quote"], 19 maxTweetLength: 280, //1/7/18 by DW 20 flJustViewMsg: false, //5/11/19 by DW 21 newMessageCallback: function (jstruct) { 22 }, 23 updatedMessageCallback: function (jstruct) { 24 }, 25 getConfigCallback: function () { 26 var emptyConfig = { 27 }; 28 return (emptyConfig); 29 }, 30 dynamicElementsCallback: function () { 31 }, 32 processTextCallback: function (s) { 33 return (s); 34 } 35 }; 36 var chatGlobals = { 37 chatlog: new Array (), 38 chatlogSerialnum: 0, 39 myChatLogSocket: undefined, 40 idLastTweet: undefined, 41 flMsgBeingEdited: false, 42 idMsgBeingEdited: undefined, 43 objMsgBeingEdited: undefined, //4/20/19 by DW 44 flSetupDynamicElements: false, 45 flMessageNotPosted: undefined, 46 whenLastAutoSave: new Date (), 47 urlDefaultIcon: "http://static.scripting.com/larryKing/images/2012/10/16/clarus.gif", //8/21/17 by DW 48 initialEditorText: "<p><br></p>", 49 flPrefsChanged: false, 50 flJsonDisplayMode: false, 51 serverStats: undefined //a copy of the stats object returned from the server 52 }; 53 54 function settingsCommand () { 55 prefsDialogShow (function () { 56 prefsChanged (); 57 }); 58 } 59 function chatGetWhen (jstruct) { 60 var when; 61 if (jstruct.pubDate !== undefined) { 62 when = jstruct.pubDate; 63 } 64 else { 65 when = jstruct.when; 66 } 67 return (new Date (when)); 68 } 69 function getWhenLink (jstruct) { 70 var whenstring = getFacebookTimeString (chatGetWhen (jstruct)); 71 if (jstruct.permalink !== undefined) { 72 whenstring = "<a href=\"" + jstruct.permalink + "\">" + whenstring + "</a>"; 73 } 74 return (whenstring); 75 } 76 function viewChatMsg (jstruct) { 77 var htmltext = "", indentlevel = 0, flAddContainer = false; 78 79 function add (s) { 80 htmltext += filledString ("\t", indentlevel) + s + "\n"; 81 } 82 function getBodyText () { 83 function removeSchmutzFromText (theText) { //8/24/17 by DW -- remove extra stuff added by medium-editor 84 if (endsWith (theText, chatGlobals.initialEditorText)) { 85 theText = stringMid (theText, 1, theText.length - chatGlobals.initialEditorText.length); 86 } 87 return (theText); 88 } 89 var bodytext = removeSchmutzFromText (jstruct.text); 90 91 function getTitle (flPeriod) { 92 let theTitle = jstruct.title; 93 if ((theTitle !== undefined) && (theTitle.length > 0)) { 94 if (flPeriod) { 95 theTitle = addPeriodAtEnd (theTitle); 96 } 97 return ("<span class=\"spChatTitle\">" + theTitle + "</span>"); 98 } 99 else { 100 return (""); 101 } 102 } 103 104 if (jstruct.enclosure !== undefined) { //see if it's an image 105 if (jstruct.enclosure.type !== undefined) { 106 if (beginsWith (jstruct.enclosure.type, "image/")) { 107 let htmltext = getTitle (false) + "<img src=\"" + jstruct.enclosure.url + "\">"; 108 return (htmltext); 109 } 110 } 111 } 112 113 if (jstruct.outline !== undefined) { 114 if (jstruct.outline.subs !== undefined) { 115 let htmltext = renderOutlineBrowser (jstruct.outline, false, undefined, undefined, true); 116 return (htmltext); 117 } 118 else { 119 bodytext = jstruct.outline.text; //single-headline outline, keep it simple --7/28/17 by DW 120 if (jstruct.outline.image !== undefined) { 121 bodytext = "<img src=\"" + jstruct.outline.image + "\" style=\"float: right; margin-left: 1px;
121 margin-top: 1px;\">" + bodytext; 122 } 123 } 124 } 125 bodytext = getTitle (true) + bodytext; 126 127 bodytext = chatOptions.processTextCallback (bodytext); 128 129 //fancy stuff with show more/show less, disabled 130 if (false) { 131 var maxchars = 400, showMore = "", remainingText = ""; 132 if (bodytext.length > maxchars) { 133 var idRemainingText = "rt" + rtSerialnum, idShowMoreLink = "sm" + rtSerialnum++; 134 showMore = " <a id=\"" + idShowMoreLink + "\" class=\"aShowMoreLink\" onclick=\"showMore ('" + idRemainingText + "', '" + idShowMoreLink + "')\">Show more</a>"; 135 bodytext = maxStringLength (bodytext, maxchars, true, false); 136 remainingText = "</p><span class=\"spHiddenText\" id=\"" + idRemainingText + "\"><p>" + stringMid (jstruct.text, bodytext.length, jstruct.text.length - bodytext.length + 1) + "</span>"; 137 } 138 bodytext = bodytext + remainingText + showMore 139 } 140 141 if (jstruct.screenname == twGetScreenName ()) { 142 bodytext = "<div class=\"divClickableMsg\" data-idmsg=\"" + jstruct.id + "\">" + bodytext + "</div>"; 143 chatGlobals.flSetupDynamicElements = true; 144 } 145 return (bodytext); 146 } 147 function getIcon () { 148 if (jstruct.urlIcon == "http://pbs.twimg.com/profile_images/888857400886906881/TfGkETi-_normal.jpg") { 149 jstruct.urlIcon = undefined; 150 } 151 if (jstruct.urlIcon === undefined) { 152 return (chatGlobals.urlDefaultIcon); //1/1/18 by DW 153 } 154 else { 155 return (jstruct.urlIcon); 156 } 157 } 158 function getAuthorLink (linktext) { 159 var authorlink; 160 if (jstruct.screenname !== undefined) { 161 authorlink = "<a href=\"https://twitter.com/" + jstruct.screenname + "/\" target=\"_blank\">" + linktext + "</a>"; 162 } 163 else { 164 authorlink = linktext; 165 } 166 return (authorlink); 167 } 168 function getCheckbox () { //1/13/18 by DW 169 if (userIsSysop ()) { 170 var theChecked = ""; //not checked initially 171 var theCheckbox = "<input type=\"checkbox\" data-id=\"" + jstruct.id + "\" value=\"xxx\" " + theChecked + ">"; 172 return ("<div class=\"divChatMsgCheckbox\">" + theCheckbox + "</div>"); 173 } 174 else { 175 return (""); 176 } 177 } 178 179 if (jstruct.text.length == 0) { //8/25/17 by DW 180 return (""); 181 } 182 183 if (jstruct.idHtmlObject === undefined) { 184 jstruct.serialnum = chatGlobals.chatlogSerialnum++; 185 jstruct.idHtmlObject = "idChatMsg" + jstruct.serialnum; 186 flAddContainer = true; 187 } 188 if (flAddContainer) { 189 add ("<div class=\"divChatMsgContainer\" id=\"" + jstruct.idHtmlObject + "\">"); indentlevel++; 190 } 191 192 if (chatGlobals.flJsonDisplayMode) { //1/8/18; by DW 193 let jsontext = jsonStringify (jstruct); 194 let jstructcopy = JSON.parse (jsontext); 195 196 function deleteValue (name) { 197 try { 198 delete jstructcopy [name]; 199 } 200 catch (err) { 201 } 202 } 203 deleteValue ("urlIcon"); 204 deleteValue ("serialnum"); 205 deleteValue ("idHtmlObject"); 206 207 jsontext = jsonStringify (jstructcopy); 208 209 jsontext = replaceAll (jsontext, "<", "<"); 210 add ("<div class=\"divJsonText\"><pre>" + jsontext + "</pre></div>"); 211 } 212 else { 213 add (getCheckbox ()); 214 add ("<table>"); indentlevel++; 215 add ("<tr>"); indentlevel++; 216 //icon 217 add ("<td class=\"tdIconInChat\">"); indentlevel++; 218 add ("<div class=\"divUserIcon\">" + getAuthorLink ("<img src=\"" + getIcon () + "\" width=\"48\" height=\"48\" border=\"0\">") + "</div>"); 219 add ("</td>"); indentlevel--; 220 //text 221 add ("<td class=\"tdTextInChat\">"); indentlevel++; 222 //top line 223 add ("<div class=\"divChatTopLine\">"); indentlevel++; 224 add ("<span class=\"spChatAuthorName\">" + getAuthorLink (jstruct.authorname) + "</span>"); 225 add ("<span class=\"spChatWhen\" id=\"idWhen" + jstruct.serialnum + "\">" + getWhenLink (jstruct) + "</span>"); 226 227 if (jstruct.idTweet !== undefined) { //9/28/16 by DW 228 var urlForTweet = "https://twitter.com/" + jstruct.screenname + "/status/" + jstruct.idTweet; 229 add ("<span class=\"spChatTweetLink\"><a href=\"" + urlForTweet + "\" target=\"_blank\">" + twStorageConsts.fontAwesomeIcon + "</a></span>"); 230 } 231 232 add ("</div>"); indentlevel--; 233 add ("<div class=\"divChatBody\">" + getBodyText () + "</div>"); 234 //linktext -- 8/15/17 by DW 235 var linktext = ""; 236 if ((jstruct.link != undefined) && (jstruct.link.length > 0)) { 237 var splitUrl = urlSplitter (trimLeading (jstruct.link, " ")); 238 var host = splitUrl.host; 239 if (beginsWith (host, "www.")) { 240 host = stringDelete (host, 1, 4); 241 } 242 linktext = "<span class=\"spLink\"><a class=\"aHost\" href=\"" + jstruct.link + "\" target=\"blank\">
242" + host + "</a></span>"; 243 } 244 //likes -- 10/8/16 by DW 245 (function () { 246 var ct = 0, likenames = "", thumbDirection = "up"; 247 if (jstruct.likes !== undefined) { 248 for (var x in jstruct.likes) { 249 ct++; 250 likenames += x + ", "; 251 } 252 } 253 254 var thumbUp = ""; 255 if (twIsTwitterConnected ()) { 256 thumbUp = "<span class=\"spThumb\"><a class=\"aLikeIcon\" data-idmsg=\"" + jstruct.id + "\"><i class=\"fa fa-thumbs-" + thumbDirection + "\"></i></a></span> "; 257 } 258 259 var ctLikes = ct + " like"; 260 261 if (ct == 0) { 262 ctLikes = ""; //get rid of 0 Likes -- 8/24/17 by DW 263 } 264 else { 265 if (ct != 1) { 266 ctLikes += "s"; 267 } 268 if (ct > 0) { 269 likenames = stringMid (likenames, 1, likenames.length - 2); //pop off comma and blank at end 270 ctLikes = "<span rel=\"tooltip\" title=\"" + likenames + "\">" + ctLikes + "</span>"; 271 } 272 } 273 274 add ("<div class=\"divChatBottomLine\">" + linktext + "<span class=\"spLikes\">" + thumbUp + ctLikes + "</span></div>"); 275 }) (); 276 add ("</td>"); indentlevel--; 277 add ("</tr>"); indentlevel--; 278 add ("</table>"); indentlevel--; 279 } 280 281 if (flAddContainer) { 282 add ("</div>"); indentlevel--; 283 } 284 return (htmltext); 285 } 286 function chatSocketStart (urlSocket, callback) { 287 if (chatGlobals.socket === undefined) { 288 chatGlobals.socket = new WebSocket (urlSocket); 289 chatGlobals.socket.onopen = function (evt) { 290 console.log ("chatGlobals.socket is open, sending \"watch chatlog\" message."); 291 viewServerStats (); 292 chatGlobals.socket.send ("watch chatlog"); 293 }; 294 chatGlobals.socket.onmessage = function (evt) { 295 var s = evt.data; 296 if (s !== undefined) { //no error 297 var verb = stringNthField (s, "\r", 1); 298 s = stringDelete (s, 1, verb.length + 1); 299 callback (verb, s); 300 } 301 }; 302 chatGlobals.socket.onclose = function (evt) { 303 console.log ("chatGlobals.socket was closed."); 304 chatGlobals.socket = undefined; 305 }; 306 chatGlobals.socket.onerror = function (evt) { 307 console.log ("chatGlobals.socket received an error"); 308 }; 309 } 310 } 311 function checkChatSocket () { 312 chatSocketStart (chatOptions.urlChatSocket, function (verb, s) { 313 console.log ("checkChatSocket: verb == " + verb); 314 switch (verb.toLowerCase ()) { 315 case "update": 316 var jstruct = JSON.parse (s); 317 for (var i = 0; i < chatGlobals.chatlog.messages.length; i++) { //see if an existing message was updated 318 var item = chatGlobals.chatlog.messages [i]; 319 if (item.id == jstruct.id) { 320 for (var x in jstruct) { //8/27/17 by DW -- don't eliminate items that aren't present in jstruct 321 item [x] = jstruct [x]; 322 } 323 324 if (isPostVisible (item)) { 325 $("#" + item.idHtmlObject).html (viewChatMsg (item)); 326 } 327 console.log ("chatlogEverySecond: existing object updated == " + jsonStringify (jstruct)); 328 chatOptions.updatedMessageCallback (jstruct); //8/11/17 by DW 329 chatGlobals.flSetupDynamicElements = true; 330 return; 331 } 332 } 333 console.log ("chatlogEverySecond: new object == " + jsonStringify (jstruct)); 334 chatGlobals.chatlog.messages.unshift (jstruct); 335 if (isPostVisible (jstruct)) { 336 $("#idChatlogViewer").prepend (viewChatMsg (jstruct)); 337 chatOptions.newMessageCallback (jstruct); //8/11/17 by DW 338 } 339 break; 340 case "stats": //5/11/19 by DW 341 chatGlobals.serverStats = JSON.parse (s); 342 viewServerStats (); 343 console.log ("checkChatSocket: chatGlobals.serverStats == " + jsonStringify (chatGlobals.serverStats)); 344 break; 345 case "rollover": 346 chatlogStart (); 347 break; 348 case "reload": 349 window.location.href = window.location.href; 350 break; 351 case "deleteitem": 352 var jstruct = JSON.parse (s); 353 deleteMessage (jstruct.id) 354 break; 355 } 356 }); 357 } 358 function serverCall (verb, params, flAuthenticated, callback, method, postbody) { 359 if (flAuthenticated === undefined) { 360 flAuthenticated = true; 361 } 362 if (params === undefined) { 363 params = new Object (); 364 } 365 if (method === undefined) { 366 method = "GET"; 367 } 368 if (flAuthenticated) { 369 params.oauth_token = localStorage.twOauthToken; 370 params.oauth_token_secret = localStorage.twOauthTokenSecret; 371 } 372 373 var apiUrl = chatOptions.urlChatServer + verb; 374 var paramString = twBuildParamList (params); 375 if (paramString.length > 0) { 376 apiUrl += "?" + paramString; 377 } 378 379 console.log ("serverCall: verb == " + verb + ", apiUrl == " + apiUrl); 380 381 $.ajax ({ 382 type: method, 383 url: apiUrl, 384 data: postbody, 385 success: function (data) { 386 if (callback !== undefined) { 387 callback (undefined, data); 388 } 389 }, 390 error: function (status, something, otherthing) { 391 console.log ("serverCall: error == " + JSON.stringify (status, undefined, 4)); 392 if (callback !== undefined) { 393 var err = { 394 code: status.status, 395 message: JSON.parse (status.responseText).message 396 }; 397 if (callback !== undefined) { 398 callback (err); 399 } 400 } 401 }, 402 dataType: "json" 403 }); 404 } 405 function isPostVisible (jstruct) { //8/23/17 by DW 406 var theName = jstruct.screenname; 407 if (theName === undefined) { //the post came from scripting news, or linkblog, or... 408 return (true); 409 } 410 if (theName == twGetScreenName ()) { //it's the user's own post 411 return (true); 412 } 413 if (!getBoolean (chatOptions.flEveryoneFollowsEnabled)) { //5/8/19 by DW 414 return (true); 415 } 416 if (getBoolean (chatOptions.everyoneFollows [theName])) { 417 return (true); 418 } 419 if (twGetScreenName () == "davewiner") { //for now I see everything 420 return (true); 421 } 422 return (false); 423 } 424 function prefsChanged () { 425 chatGlobals.flPrefsChanged = true; 426 } 427 function getPrefs (callback) { 428 if (twIsTwitterConnected ()) { 429 serverCall ("getprefs", undefined, true, function (err, data) { 430 if (!err) { 431 appPrefs = data; 432 console.log ("getPrefs: appPrefs == " + jsonStringify (appPrefs)); 433 } 434 callback (); 435 }); 436 } 437 else { 438 callback (); 439 } 440 } 441 function savePrefs (callback) { 442 var whenstart = new Date (); 443 var params = { 444 jsontext: jsonStringify (appPrefs) 445 } 446 serverCall ("saveprefs", params, true, function (err, data) { 447 if (!err) { 448 console.log ("savePrefs: " + secondsSince (whenstart) + " secs"); 449 } 450 if (callback !== undefined) { 451 callback (); 452 } 453 }); 454 } 455 function getConfig () { 456 return (chatOptions.getConfigCallback ()); 457 } 458 function showEditControls () { 459 $("#idStuffThatsHiddenWhenNotActive").css ("visibility", "visible"); 460 $("#idPostButton").css ("visibility", "visible"); 461 } 462 function hideEditControls () { 463 $("#idStuffThatsHiddenWhenNotActive").css ("visibility", "hidden"); 464 $("#idPostButton").css ("visibility", "hidden"); 465 } 466 function visitCheckedMessages (callback) { 467 $(".divChatMsgCheckbox input").each (function () { 468 if ($(this).prop ("checked")) { 469 var id = $(this).data ("id"); 470 console.log ("visitCheckedMessages: id == " + id); 471 if (callback !== undefined) { 472 callback (id); 473 } 474 } 475 }); 476 } 477 function likeClick (idMessage, callback) { 478 var params = { 479 id: idMessage 480 }; 481 serverCall ("like", params, true, callback); 482 } 483 function chatlogDeleteItem (idMessage, callback) { 484 var params = { 485 id: idMessage 486 }; 487 serverCall ("delete", params, true, callback); 488 } 489 function getChatlog (callback) { 490 serverCall ("getchatlog", undefined, false, callback); 491 } 492 function getMyScreenName (callback) { 493 serverCall ("getmyscreenname", undefined, true, function (err, data) { 494 callback (data); 495 }); 496 } 497 function getMainEditorText (flStripMarkup) { 498 var s = $("#idBodyEditor").html (); 499 if (flStripMarkup) { 500 s = stripMarkup (s); 501 } 502 s = replaceAll (s, " ", " "); //sometimes medium-editor appends a at the end 503 return (s); 504 } 505 function getMainEditorTextLength (flStripMarkup) { 506 if (flStripMarkup === undefined) {
507 flStripMarkup = true; 508 } 509 return (getMainEditorText (flStripMarkup).length); 510 } 511 function getHashTag () { //includes the space separating the tag from the text 512 var s = trimWhitespace (appPrefs.hashTagForTwitter); 513 if (s.length == 0) { 514 return (""); 515 } 516 return (" " + s); 517 } 518 function setEditorText (bodytext) { 519 $("#idBodyEditor").html (bodytext); 520 var editorBody = new MediumEditor (".divBodyEditor", { 521 placeholder: { 522 text: " " + chatOptions.editorPlaceholderText 523 }, 524 toolbar: { 525 buttons: chatOptions.defaultEditorButtons, 526 }, 527 buttonLabels: "fontawesome", 528 imageDragging: false, //10/18/16 by DW 529 autoLink: true 530 }); 531 } 532 function showMore (idRemainingText, idShowMoreLink) { 533 console.log ("showMore: idRemainingText == " + idRemainingText + ", idShowMoreLink == " + idShowMoreLink); 534 if ($("#" + idRemainingText).css ("display") == "inline") { //it's visible, hide it 535 $("#" + idRemainingText).css ("display", "none"); 536 $("#" + idShowMoreLink).text ("Show more"); 537 } 538 else { //it's hidden, make it visible 539 $("#" + idRemainingText).css ("display", "inline"); 540 $("#" + idShowMoreLink).text ("Show less"); 541 } 542 } 543 function getEditorJstruct () { 544 var jstruct = { 545 text: getMainEditorText (), 546 authorname: chatOptions.userInfoFromTwitter.name, 547 screenname: twGetScreenName (), 548 urlIcon: chatOptions.userInfoFromTwitter.profile_image_url, 549 when: new Date (), 550 idTweet: chatGlobals.idLastTweet 551 }; 552 return (jstruct); 553 } 554 function chatlogRollover (callback) { 555 serverCall ("rollover", undefined, true, callback); 556 } 557 function chatlogReload (callback) { 558 serverCall ("reload", undefined, true, callback); 559 } 560 function updateCharCount () { 561 var ct = getMainEditorTextLength (); 562 if (appPrefs.flSendHashTagToTwitter) { 563 ct += getHashTag ().length 564 } 565 if (appPrefs.flEchoPostsToTwitter) { 566 if (ct > chatOptions.maxTweetLength) { 567 ct = "<span class=\"spRedCharCount\">" + ct + "</span>"; 568 } 569 } 570 $("#idCharCount").html (ct); 571 } 572 function postNewMsg (callback) { 573 if (getMainEditorTextLength () > 0) { 574 var postbody = jsonStringify (getEditorJstruct ()); 575 serverCall ("post", undefined, true, callback, "POST", postbody); 576 } 577 else { 578 if (callback !== undefined) { 579 callback (undefined); 580 } 581 } 582 } 583 function updateExistingMsg (callback) { 584 console.log ("updateExistingMsg: chatGlobals.idMsgBeingEdited == " + chatGlobals.idMsgBeingEdited); 585 var params = { 586 id: chatGlobals.idMsgBeingEdited, 587 text: getMainEditorText () 588 }; 589 serverCall ("updatetext", params, true, callback); 590 } 591 function resetEditor () { 592 setEditorText (chatGlobals.initialEditorText); 593 updateCharCount (); 594 setPostButtonText (); 595 hideEditControls (); 596 $("#idBodyEditor").blur (); //take the focus off the editor 597 } 598 function cancelEdit () { 599 chatGlobals.flMsgBeingEdited = false; 600 chatGlobals.idMsgBeingEdited = undefined; 601 if (chatGlobals.objMsgBeingEdited) { //4/20/19 by DW 602 $(chatGlobals.objMsgBeingEdited).removeClass ("divMsgBeingEdited"); //4/20/19 by DW 603 chatGlobals.objMsgBeingEdited = undefined; 604 } 605 resetEditor (); 606 } 607 function postButtonClick () { 608 if (chatOptions.flReadOnlyMode) { 609 alertDialog ("Sorry, " + getConfig ().productnameForDisplay + " is operating in read-only mode at this time."); 610 } 611 else { 612 function doPost () { 613 postNewMsg (function (err, data) { 614 console.log ("postButtonClick: the server returned == " + jsonStringify (data)); 615 resetEditor (); 616 }); 617 } 618 if (chatGlobals.flMsgBeingEdited) { 619 updateExistingMsg (function (err, data) { 620 console.log ("postButtonClick: the server returned == " + jsonStringify (data)); 621 chatGlobals.flMsgBeingEdited = false; 622 chatGlobals.idMsgBeingEdited = undefined; 623 resetEditor (); 624 }); 625 } 626 else { 627 if (appPrefs.flEchoPostsToTwitter) { 628 var s = getMainEditorText (true); 629 s = replaceAll (s, " ", " "); 630 s = trimWhitespace (s); 631 if (s.length > 0) { 632 if (appPrefs.flSendHashTagToTwitter) { 633 s += getHashTag (); 634 } 635 console.log ("postButtonClick: what we're tweeting == " + s); 636 twTweet (s, "", function (data) { 637 chatGlobals.idLastTweet = data.id_str; //set global 638 doPost (); 639 }); 640 } 641 } 642 else { 643 chatGlobals.idLastTweet = undefined; 644 doPost (); 645 } 646 } 647 } 648 } 649 function cancelButtonClick () { 650 if (chatGlobals.flMsgBeingEdited) { 651 cancelEdit (); 652 } 653 } 654 function getServerStats (callback) { 655 serverCall ("stats", undefined, false, function (err, server) { 656 chatGlobals.serverStats = server; 657 if (callback !== undefined) { 658 callback (err, server); 659 } 660 }); 661 } 662 function setupDynamicElements () { 663 $("#idChatlogViewer *").off (); //remove all pre-existing handlers attached to the chatlog -- 1/7/18 by DW 664 $('[rel="tooltip"]').tooltip ({ 665 animation: true, 666 placement: "right" 667 }); 668 $("a[rel=tooltip]").tooltip ({ 669 live: true 670 }); 671 $(".divClickableMsg").on ("click", function (event) { 672 var idmsg = $(this).data ("idmsg"); 673 $(this).addClass ("divMsgBeingEdited"); //4/20/19 by DW 674 chatGlobals.objMsgBeingEdited = this; //4/20/19 by DW 675 editMessage (idmsg); 676 event.stopPropagation (); 677 }); 678 $(".aLikeIcon").on ("click", function (event) { 679 var idmsg = $(this).data ("idmsg"); 680 console.log ("You clicked on a .aLikeIcon, iidmsg == " + idmsg); 681 likeClick (idmsg); 682 event.stopPropagation (); 683 }); 684 $("#idBodyEditor").focus (function (event) { 685 showEditControls (); //4/20/19 by DW 686 chatGlobals.flMessageNotPosted = true; 687 showEditControls (); 688 }); 689 $("#idBodyEditor").blur (function (event) { 690 console.log ("You clicked away from #idBodyEditor."); 691 if ($("#medium-editor-toolbar-1").css ("visibility") == "hidden") { //don't hide controls if the toolbar got the click -- 1/12/18 by DW 692 hideEditControls (); 693 } 694 }); 695 $("#idBodyEditor").keyup (function (event) { 696 if (event.which == 27) { //escape key 697 console.log ("You pressed the ESC key in an editing text area."); 698 if (chatGlobals.flMsgBeingEdited) { 699 cancelEdit (); 700 } 701 else { 702 hideEditControls (); 703 } 704 $("#idBodyEditor").blur (); //take the focus off the editor
705 event.stopPropagation (); 706 return; 707 } 708 updateCharCount (); 709 }); 710 $("#idBodyEditor").keydown (function () { 711 if (event.which == 13) { //return key 712 if (appPrefs.flPostOnReturnKey) { 713 postButtonClick (); 714 event.stopPropagation (); 715 return; 716 } 717 } 718 }); 719 $("#idTweetCheckbox").click (function () { 720 console.log ("You clicked the checkbox, its value is " + this.checked); 721 appPrefs.flEchoPostsToTwitter = getBoolean (this.checked); 722 updateCharCount (); 723 prefsChanged (); 724 }); 725 726 $("#idPostButton").mousedown (function (event) { 727 console.log ("You clicked on the post button."); 728 if (chatGlobals.flMessageNotPosted) { //it's a new click 729 postButtonClick (); 730 event.stopPropagation (); 731 event.preventDefault (); 732 chatGlobals.flMessageNotPosted = false; 733 } 734 else { 735 console.log ("Repeat mousedown event, not processed."); 736 } 737 }); 738 $("#idCancelButton").mousedown (function (event) { 739 cancelButtonClick (); 740 $("#idBodyEditor").blur (); //take the focus off the editor 741 event.stopPropagation (); 742 event.preventDefault (); 743 }); 744 745 if (chatOptions.dynamicElementsCallback !== undefined) { //8/21/17 by DW 746 chatOptions.dynamicElementsCallback (); 747 } 748 } 749 function findMessage (id, callback) { 750 for (var i = 0; i < chatGlobals.chatlog.messages.length; i++) { 751 var item = chatGlobals.chatlog.messages [i]; 752 if (item.id == id) { 753 callback (item, i); 754 return; 755 } 756 } 757 callback (undefined); 758 } 759 function viewChatlog () { 760 var htmltext = ""; 761 for (var i = 0; i < chatGlobals.chatlog.messages.length; i++) { 762 let item = chatGlobals.chatlog.messages [i]; 763 if (isPostVisible (item)) { 764 item.idHtmlObject = undefined; 765 htmltext += viewChatMsg (item); 766 } 767 } 768 $("#idChatlogViewer").html (htmltext); 769 chatGlobals.flSetupDynamicElements = true; 770 } 771 function toggleJsonDisplayMode () { 772 chatGlobals.flJsonDisplayMode = !chatGlobals.flJsonDisplayMode; 773 viewChatlog (); 774 } 775 function chatlogStart (callback) { 776 const whenstart = new Date (); 777 getServerStats (function () { 778 getChatlog (function (err, chatlogSubset) { 779 if (!err) { 780 console.log ("chatlogStart: " + chatlogSubset.messages.length + " messages, took " + secondsSince (whenstart) + " secs."); 781 chatGlobals.chatlog = chatlogSubset; 782 viewChatlog (); 783 if (callback !== undefined) { 784 callback (); 785 } 786 } 787 }); 788 }); 789 } 790 function userIsSysop () { //4/21/19 by DW 791 if (chatGlobals.serverStats === undefined) { //5/11/19 by DW 792 return (false); 793 } 794 else { 795 return (chatGlobals.serverStats.owner == twGetScreenName ()); 796 } 797 } 798 function viewSysopMenu (server) { 799 var att = (userIsSysop ()) ? "block" : "none"; 800 $("#idSysopMenu").css ("display", att); 801 } 802 function viewServerStats () { 803 var server = chatGlobals.serverStats; 804 $("#idServerStats").html ("Server: " + server.productName + " v" + server.version + ", open sockets: " + server.ctSockets + "."); 805 $("#idVersionNumber").text ("v" + server.version); 806 viewSysopMenu (server); //depends on server.owner 807 } 808 function setPostButtonText () { 809 var theText, cancelVisible; 810 if (chatGlobals.flMsgBeingEdited) { 811 theText = "Update"; 812 cancelVisible = "visible"; 813 } 814 else { 815 theText = "Post"; 816 cancelVisible = "hidden"; 817 } 818 $("#idPostButton").html ("<i class=\"fa fa-check\"></i> " + theText); 819 820 $("#idCancelButton").css ("visibility", cancelVisible); 821 } 822 function editMessage (idmsg) { 823 findMessage (idmsg, function (item) { 824 console.log ("editMessage: item == " + jsonStringify (item)); 825 setEditorText (item.text); 826 $("#idBodyEditor").focus (); 827 chatGlobals.flMsgBeingEdited = true; 828 chatGlobals.idMsgBeingEdited = idmsg; 829 setPostButtonText (); 830 }); 831 } 832 function deleteMessage (idmsg) { 833 findMessage (idmsg, function (item, ix) { 834 $("#" + item.idHtmlObject).remove (); 835 chatGlobals.chatlog.messages.splice (ix, 1); 836 }); 837 } 838 function showHideEditor () { 839 var displayval; 840 if (twIsTwitterConnected ()) { 841 displayval = "block"; 842 } 843 else { 844 displayval = "none"; 845 } 846 if ($("#idEditorSection").css ("display") !== displayval) { //you only see the editor if you're logged in 847 $("#idEditorSection").css ("display", displayval); 848 } 849 850 851 } 852 function initEditor (initialText) { 853 if (initialText !== undefined) { 854 setEditorText (initialText); //9/22/16 by DW 855 } 856 $("#idTweetCheckbox").attr ("checked", appPrefs.flEchoPostsToTwitter); 857 $("#idHashtagCheckbox").attr ("checked", appPrefs.flSendHashTagToTwitter); 858 updateCharCount (); //9/23/16 by DW 859 showHideEditor (); 860 } 861 function setValuesFromConfig () { 862 $("#idBodyEditor").attr ("data-placeholder", " " + chatOptions.editorPlaceholderText); 863 } 864 function everyMinute () { 865 var now = new Date (); 866 for (var i = chatGlobals.chatlog.messages.length - 1; i >= 0; i--) { 867 var item = chatGlobals.chatlog.messages [i]; 868 $("#idWhen" + item.serialnum).html (getWhenLink (item)); 869 } 870 } 871 function everySecond () { 872 checkChatSocket (); 873 if (chatGlobals.flSetupDynamicElements) { 874 setupDynamicElements (); 875 chatGlobals.flSetupDynamicElements = false;
876 } 877 if (chatOptions.flAutoSave) { 878 if (!chatGlobals.flMsgBeingEdited) { 879 if (secondsSince (chatGlobals.whenLastAutoSave) > chatOptions.minSecsBetwAutoSaves) { 880 var chattext = getMainEditorText (); 881 if (chattext != appPrefs.savedTextArea) { 882 appPrefs.savedTextArea = chattext; 883 chatGlobals.whenLastAutoSave = new Date (); 884 prefsChanged (); 885 } 886 } 887 } 888 } 889 if (chatGlobals.flPrefsChanged) { 890 savePrefs (); 891 chatGlobals.flPrefsChanged = false; 892 } 893 showHideEditor (); //1/3/18 by DW 894 } 895 896 console.log ("chatApp"); 897 this.rollover = chatlogRollover; 898 this.reload = chatlogReload; 899 this.deleteItem = chatlogDeleteItem; 900 this.likeClick = likeClick; 901 this.serverCall = serverCall; //1/4/18 by DW 902 this.initEditor = initEditor; //1/4/18 by DW 903 this.chatGlobals = chatGlobals; //1/7/18 by DW 904 this.prefs = appPrefs; //1/7/18 by DW 905 this.prefsChanged = prefsChanged; //1/9/18 by DW 906 this.toggleJsonDisplayMode = toggleJsonDisplayMode; //1/8/18 by DW 907 this.settingsCommand = settingsCommand; //1/9/18 by DW 908 this.visitCheckedMessages = visitCheckedMessages; //1/13/18 by DW 909 this.viewChatMsg = viewChatMsg; //5/11/19 by DW 910 this.getOptions = function () { 911 return (chatOptions); 912 }; 913 for (var x in options) { 914 chatOptions [x] = options [x]; 915 } 916 if (!chatOptions.flJustViewMsg) { 917 showHideEditor (); //4/20/19 by DW -- moved this to the beginning as an experiment 918 chatlogStart (function () { 919 setValuesFromConfig (); 920 getPrefs (function () { 921 initEditor (appPrefs.savedTextArea); 922 self.setInterval (everySecond, 1000); 923 runEveryMinute (everyMinute); 924 callback (); 925 }); 926 }); 927 } 928 else { //5/11/19 by DW 929 callback (this); 930 } 931 } 932
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.