Diff
Not logged in

Differences From Artifact [88d5b70726]:

To Artifact [43c12368fd]:


793
794
795
796
797
798
799





800
801
802
803
804
805
806
807
808
809
810
811

















812
813

814
815
816
817
818
819
820
821
822

823
824
825
826
827
828
829
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834

835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852







+
+
+
+
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+









+







      }else{
        e = this.getMessageElemById(id);
      }
      if(!e || !id) return false;
      else if(e.$isToggling) return;
      e.$isToggling = true;
      const content = e.querySelector('.content-target');
      if(!content){
        console.warn("Should not be possible: trying to toggle text",
                     "mode of a message with no .content-target.", e);
        return;
      }
      if(!content.$elems){
        content.$elems = [
          content.firstElementChild, // parsed elem
          undefined // plaintext elem
        ];
      }else if(content.$elems[1]){
        // We have both content types. Simply toggle them.
        const child = (
          content.firstElementChild===content.$elems[0]
            ? content.$elems[1]
            : content.$elems[0]
        );
        D.clearElement(content);
        if(child===content.$elems[1]){
          /* When showing the unformatted version, inject a
             copy-to-clipboard button. This is a workaround for
             mouse-copying from that field collecting twice as many
             newlines as it should (for unknown reasons). */
          const cpId = 'copy-to-clipboard-'+id;
          /* ^^^ copy button element ID, needed for LABEL element
             pairing.  Recall that we destroy all child elements of
             `content` each time we hit this block, so we can reuse
             that element ID on subsequent toggles. */
          const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId);
          F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
          const lblCp = D.label(cpId, "Copy unformatted text");
          lblCp.addEventListener('click',()=>btnCp.click(), false);
          D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
        }
        delete e.$isToggling;
        D.append(D.clearElement(content), child);
        D.append(content, child);
        return;
      }
      // We need to fetch the plain-text version...
      const self = this;
      F.fetch('chat-fetch-one',{
        urlParams:{ name: id, raw: true},
        responseType: 'json',
        onload: function(msg){
          content.$elems[1] = D.append(D.pre(),msg.xmsg);
          content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/;
          self.toggleTextMode(e);
        },
        aftersend:function(){
          delete e.$isToggling;
          Chat.ajaxEnd();
        }
      });
1311
1312
1313
1314
1315
1316
1317



1318
1319
1320
1321
1322






1323
1324
1325
1326
1327
1328
1329
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343





1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356







+
+
+
-
-
-
-
-
+
+
+
+
+
+







                    e = n;
                  }
                  eMsg.scrollIntoView();
                }
              ));
              const toolbar2 = D.addClass(D.div(), 'toolbar');
              D.append(this.e, toolbar2);
              if(eMsg.querySelector('.content-target')){
                /* ^^^ messages with only an embedded image have no
                   .content-target area. */
              D.append(toolbar2, D.button(
                "Toggle text mode", function(){
                  self.hide();
                  Chat.toggleTextMode(eMsg);
                }));
                D.append(toolbar2, D.button(
                  "Toggle text mode", function(){
                    self.hide();
                    Chat.toggleTextMode(eMsg);
                  }));
              }
              if(eMsg.dataset.xfrom){
                /* Add a link to the /timeline filtered on this user. */
                const timelineLink = D.attr(
                  D.a(F.repoUrl('timeline',{
                    u: eMsg.dataset.xfrom,
                    y: 'a'
                  }), "User's Timeline"),
1528
1529
1530
1531
1532
1533
1534

1535
1536
1537
1538
1539

1540
1541

1542
1543
1544
1545
1546
1547

1548
1549






1550
1551
1552
1553

1554
1555
1556
1557
1558
1559
1560
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566

1567
1568

1569
1570
1571
1572
1573
1574
1575
1576


1577
1578
1579
1580
1581
1582
1583
1584
1585

1586
1587
1588
1589
1590
1591
1592
1593







+




-
+

-
+






+
-
-
+
+
+
+
+
+



-
+







     and/or the file attachment field to the server. If both are
     empty, this is a no-op.
  */
  Chat.submitMessage = function f(){
    if(!f.spaces){
      f.spaces = /\s+$/;
      f.markdownContinuation = /\\\s+$/;
      f.spaces2 = /\s{3,}$/;
    }
    this.setCurrentView(this.e.viewMessages);
    const fd = new FormData();
    const fallback = {msg: this.inputValue()};
    var msg = fallback.msg.trim();
    var msg = fallback.msg;
    if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){
      /* Cosmetic: trim whitespace from the ends of lines to try to
      /* Cosmetic: trim most whitespace from the ends of lines to try to
         keep copy/paste from terminals, especially wide ones, from
         forcing a horizontal scrollbar on all clients. This breaks
         markdown's use of blackslash-space-space for paragraph
         continuation, but *not* doing this affects all clients every
         time someone pastes in console copy/paste from an affected
         platform. We seem to have narrowed to the console pasting
         problem to users of tmux together with certain apps (vim, at
         problem to users of tmux. Most consoles don't behave
         that way. */
         a minimum). Most consoles don't behave that way.

         We retain two trailing spaces so that markdown conventions
         which use end-of-line spacing aren't broken by this
         stripping.
      */
      const xmsg = msg.split('\n');
      xmsg.forEach(function(line,ndx){
        if(!f.markdownContinuation.test(line)){
          xmsg[ndx] = line.trimRight();
          xmsg[ndx] = line.replace(f.spaces2, '  ');
        }
      });
      msg = xmsg.join('\n');
    }
    if(msg) fd.set('msg',msg);
    const file = BlobXferState.blob || this.e.inputFile.files[0];
    if(file) fd.set("file", file);