Fossil

Check-in [593d3a3a1e]
Login

Check-in [593d3a3a1e]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Simplified and consolidated how /chat internally manages its 3 separate main views, with an eye towards making it easy to add additional views. No user-visible changes.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 593d3a3a1e5455a40663ce45dc50915d6bcd64f206ffa6f803bc2553628c3913
User & Date: stephan 2021-09-22 11:15:21.064
Context
2021-09-22
12:22
Micro-adjustments to /chat CSS to squeeze a tiny bit more space from the bottom of the screen. ... (check-in: 62deb8f794 user: stephan tags: trunk)
11:15
Simplified and consolidated how /chat internally manages its 3 separate main views, with an eye towards making it easy to add additional views. No user-visible changes. ... (check-in: 593d3a3a1e user: stephan tags: trunk)
08:43
Revert part of the previous commit so that only buttons, not textareas an input fields, are affected. ... (check-in: 15d58775a7 user: stephan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/chat.c.
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  @        your environment.
  @       </div>
  @       <input type="file" name="file" id="chat-input-file">
  @     </div>
  @     <div id="chat-drop-details"></div>
  @   </div>
  @ </div>
  @ <div id='chat-preview' class='hidden'>
  @  <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
  @  <div id='chat-preview-content' class='message-widget-content'></div>
  @  <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div>
  @ </div>
  @ <div id='chat-config' class='hidden'>
  @ <div id='chat-config-options'></div>
    /* ^^^populated client-side */
  @ <button>Close Settings</button>
  @ </div>
  @ <div id='chat-messages-wrapper'>
  /* New chat messages get inserted immediately after this element */
  @ <span id='message-inject-point'></span>
  @ </div>
  fossil_free(zProjectName);
  builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
                              "pikchr", "confirmer", NULL);
  /* Always in-line the javascript for the chat page */







|




|




|







179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  @        your environment.
  @       </div>
  @       <input type="file" name="file" id="chat-input-file">
  @     </div>
  @     <div id="chat-drop-details"></div>
  @   </div>
  @ </div>
  @ <div id='chat-preview' class='hidden chat-view'>
  @  <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
  @  <div id='chat-preview-content' class='message-widget-content'></div>
  @  <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div>
  @ </div>
  @ <div id='chat-config' class='hidden chat-view'>
  @ <div id='chat-config-options'></div>
    /* ^^^populated client-side */
  @ <button>Close Settings</button>
  @ </div>
  @ <div id='chat-messages-wrapper' class='chat-view'>
  /* New chat messages get inserted immediately after this element */
  @ <span id='message-inject-point'></span>
  @ </div>
  fossil_free(zProjectName);
  builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
                              "pikchr", "confirmer", NULL);
  /* Always in-line the javascript for the chat page */
Changes to src/chat.js.
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
      e:{/*map of certain DOM elements.*/
        messageInjectPoint: E1('#message-inject-point'),
        pageTitle: E1('head title'),
        loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
        inputWrapper: E1("#chat-input-area"),
        inputLine: E1('#chat-input-line'),
        fileSelectWrapper: E1('#chat-input-file-area'),
        messagesWrapper: E1('#chat-messages-wrapper'),
        btnSubmit: E1('#chat-message-submit'),
        inputSingle: E1('#chat-input-single'),
        inputMulti: E1('#chat-input-multi'),
        inputCurrent: undefined/*one of inputSingle or inputMulti*/,
        inputFile: E1('#chat-input-file'),
        contentDiv: E1('div.content'),
        configArea: E1('#chat-config'),
        previewArea: E1('#chat-preview'),
        previewContent: E1('#chat-preview-content'),
        btnPreview: E1('#chat-preview-button')

      },
      me: F.user.name,
      mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
      mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
      pageIsActive: 'visible'===document.visibilityState,
      changesSincePageHidden: 0,
      notificationBubbleColor: 'white',







|






|
|

|
>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
      e:{/*map of certain DOM elements.*/
        messageInjectPoint: E1('#message-inject-point'),
        pageTitle: E1('head title'),
        loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
        inputWrapper: E1("#chat-input-area"),
        inputLine: E1('#chat-input-line'),
        fileSelectWrapper: E1('#chat-input-file-area'),
        viewMessages: E1('#chat-messages-wrapper'),
        btnSubmit: E1('#chat-message-submit'),
        inputSingle: E1('#chat-input-single'),
        inputMulti: E1('#chat-input-multi'),
        inputCurrent: undefined/*one of inputSingle or inputMulti*/,
        inputFile: E1('#chat-input-file'),
        contentDiv: E1('div.content'),
        viewConfig: E1('#chat-config'),
        viewPreview: E1('#chat-preview'),
        previewContent: E1('#chat-preview-content'),
        btnPreview: E1('#chat-preview-button'),
        views: document.querySelectorAll('.chat-view')
      },
      me: F.user.name,
      mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
      mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
      pageIsActive: 'visible'===document.visibilityState,
      changesSincePageHidden: 0,
      notificationBubbleColor: 'white',
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
        if(this.e.inputCurrent === this.e.inputSingle){
          this.e.inputCurrent = this.e.inputMulti;
          this.e.inputLine.classList.remove('single-line');
        }else{
          this.e.inputCurrent = this.e.inputSingle;
          this.e.inputLine.classList.add('single-line');
        }
        const m = this.e.messagesWrapper,
              sTop = m.scrollTop,
              mh1 = m.clientHeight;
        D.addClass(old, 'hidden');
        D.removeClass(this.e.inputCurrent, 'hidden');
        const mh2 = m.clientHeight;
        m.scrollTo(0, sTop + (mh1-mh2));
        this.e.inputCurrent.value = old.value;







|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        if(this.e.inputCurrent === this.e.inputSingle){
          this.e.inputCurrent = this.e.inputMulti;
          this.e.inputLine.classList.remove('single-line');
        }else{
          this.e.inputCurrent = this.e.inputSingle;
          this.e.inputLine.classList.add('single-line');
        }
        const m = this.e.viewMessages,
              sTop = m.scrollTop,
              mh1 = m.clientHeight;
        D.addClass(old, 'hidden');
        D.removeClass(this.e.inputCurrent, 'hidden');
        const mh2 = m.clientHeight;
        m.scrollTo(0, sTop + (mh1-mh2));
        this.e.inputCurrent.value = old.value;
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
        return this;
      },
      /* Injects DOM element e as a new row in the chat, at the oldest
         end of the list if atEnd is truthy, else at the newest end of
         the list. */
      injectMessageElem: function f(e, atEnd){
        const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
              holder = this.e.messagesWrapper,
              prevMessage = this.e.newestMessage;
        if(atEnd){
          const fe = mip.nextElementSibling;
          if(fe) mip.parentNode.insertBefore(e, fe);
          else D.append(mip.parentNode, e);
        }else{
          D.append(holder,e);







|







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
        return this;
      },
      /* Injects DOM element e as a new row in the chat, at the oldest
         end of the list if atEnd is truthy, else at the newest end of
         the list. */
      injectMessageElem: function f(e, atEnd){
        const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
              holder = this.e.viewMessages,
              prevMessage = this.e.newestMessage;
        if(atEnd){
          const fe = mip.nextElementSibling;
          if(fe) mip.parentNode.insertBefore(e, fe);
          else D.append(mip.parentNode, e);
        }else{
          D.append(holder,e);
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
      },
      /** Tries to scroll the message area to...
          <0 = top of the message list, >0 = bottom of the message list,
          0 == the newest message (normally the same position as >1).
      */
      scrollMessagesTo: function(where){
        if(where<0){
          Chat.e.messagesWrapper.scrollTop = 0;
        }else if(where>0){
          Chat.e.messagesWrapper.scrollTop = Chat.e.messagesWrapper.scrollHeight;
        }else if(Chat.e.newestMessage){
          Chat.e.newestMessage.scrollIntoView(false);
        }
      },
      toggleChatOnlyMode: function(){
        return this.chatOnlyMode(!this.isChatOnlyMode());
      },
      messageIsInView: function(e){
        return e ? overlapsElemView(e, this.e.messagesWrapper) : false;
      },
      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),
        getBool: (k,dflt)=>F.storage.getBool(k,dflt),
        set: (k,v)=>F.storage.set(k,v),
        /* Toggles the boolean setting specified by k. Returns the
           new value.*/







|

|








|







336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
      },
      /** Tries to scroll the message area to...
          <0 = top of the message list, >0 = bottom of the message list,
          0 == the newest message (normally the same position as >1).
      */
      scrollMessagesTo: function(where){
        if(where<0){
          Chat.e.viewMessages.scrollTop = 0;
        }else if(where>0){
          Chat.e.viewMessages.scrollTop = Chat.e.viewMessages.scrollHeight;
        }else if(Chat.e.newestMessage){
          Chat.e.newestMessage.scrollIntoView(false);
        }
      },
      toggleChatOnlyMode: function(){
        return this.chatOnlyMode(!this.isChatOnlyMode());
      },
      messageIsInView: function(e){
        return e ? overlapsElemView(e, this.e.viewMessages) : false;
      },
      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),
        getBool: (k,dflt)=>F.storage.getBool(k,dflt),
        set: (k,v)=>F.storage.set(k,v),
        /* Toggles the boolean setting specified by k. Returns the
           new value.*/
393
394
395
396
397
398
399











400
401
402
403
404
405
406
         this.
      */
      setNewMessageSound: function f(uri){
        delete this.playNewMessageSound.audio;
        this.playNewMessageSound.uri = uri;
        this.settings.set('audible-alert', uri);
        return this;











      }
    };
    F.fetch.beforesend = ()=>cs.ajaxStart();
    F.fetch.aftersend = ()=>cs.ajaxEnd();
    cs.e.inputCurrent = cs.e.inputSingle;
    /* Install default settings... */
    Object.keys(cs.settings.defaults).forEach(function(k){







>
>
>
>
>
>
>
>
>
>
>







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
         this.
      */
      setNewMessageSound: function f(uri){
        delete this.playNewMessageSound.audio;
        this.playNewMessageSound.uri = uri;
        this.settings.set('audible-alert', uri);
        return this;
      },
      /**
         Expects e to be one of the elements in this.e.views.
         The 'hidden' class is removed from e and added to
         all other elements in that list. Returns e.
      */
      setCurrentView: function(e){
        this.e.views.forEach(function(E){
          if(e!==E) D.addClass(E,'hidden');
        });
        return this.e.currentView = D.removeClass(e,'hidden');
      }
    };
    F.fetch.beforesend = ()=>cs.ajaxStart();
    F.fetch.aftersend = ()=>cs.ajaxEnd();
    cs.e.inputCurrent = cs.e.inputSingle;
    /* Install default settings... */
    Object.keys(cs.settings.defaults).forEach(function(k){
609
610
611
612
613
614
615

616
617
618
619
620
621
622
    };
    document.addEventListener('visibilitychange', function(ev){
      cs.pageIsActive = ('visible' === document.visibilityState);
      if(cs.pageIsActive){
        cs.e.pageTitle.innerText = cs.pageTitleOrig;
      }
    }, true);

    return cs;
  })()/*Chat initialization*/;

  /**
     Custom widget type for rendering messages (one message per
     instance). These are modelled after FIELDSET elements but we
     don't use FIELDSET because of cross-browser inconsistencies in







>







621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
    };
    document.addEventListener('visibilitychange', function(ev){
      cs.pageIsActive = ('visible' === document.visibilityState);
      if(cs.pageIsActive){
        cs.e.pageTitle.innerText = cs.pageTitleOrig;
      }
    }, true);
    cs.setCurrentView(cs.e.viewMessages);
    return cs;
  })()/*Chat initialization*/;

  /**
     Custom widget type for rendering messages (one message per
     instance). These are modelled after FIELDSET elements but we
     don't use FIELDSET because of cross-browser inconsistencies in
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
     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+$/;
    }
    this.revealPreview(false);
    const fd = new FormData();
    var msg = this.inputValue().trim();
    if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){
      /* Cosmetic: trim 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. */
      const xmsg = msg.split('\n');







|







972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
     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+$/;
    }
    this.setCurrentView(this.e.viewMessages);
    const fd = new FormData();
    var msg = this.inputValue().trim();
    if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){
      /* Cosmetic: trim 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. */
      const xmsg = msg.split('\n');
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055

  (function(){/*Set up #chat-settings-button */
    const settingsButton = document.querySelector('#chat-settings-button');
    const optionsMenu = E1('#chat-config-options');
    const cbToggle = function(ev){
      ev.preventDefault();
      ev.stopPropagation();
      if(Chat.e.configArea.classList.contains('hidden')){
        D.removeClass(Chat.e.configArea, 'hidden');
        D.addClass([Chat.e.messagesWrapper, Chat.e.previewArea], 'hidden');
      }else{
        D.addClass(Chat.e.configArea, 'hidden');
        D.removeClass(Chat.e.messagesWrapper, 'hidden');
      }
      return false;
    };
    D.attr(settingsButton, 'role', 'button').addEventListener('click', cbToggle, false);
    Chat.e.configArea.querySelector('button').addEventListener('click', cbToggle, false);
    /* Settings menu entries... */
    const settingsOps = [{
      label: "Multi-line input",
      boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
      persistentSetting: 'edit-multiline',
      callback: function(){
        Chat.inputToggleSingleMulti();







<
|
<
<
|
<
<



|







1044
1045
1046
1047
1048
1049
1050

1051


1052


1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063

  (function(){/*Set up #chat-settings-button */
    const settingsButton = document.querySelector('#chat-settings-button');
    const optionsMenu = E1('#chat-config-options');
    const cbToggle = function(ev){
      ev.preventDefault();
      ev.stopPropagation();

      Chat.setCurrentView(Chat.e.currentView===Chat.e.viewConfig


                          ? Chat.e.viewMessages : Chat.e.viewConfig);


      return false;
    };
    D.attr(settingsButton, 'role', 'button').addEventListener('click', cbToggle, false);
    Chat.e.viewConfig.querySelector('button').addEventListener('click', cbToggle, false);
    /* Settings menu entries... */
    const settingsOps = [{
      label: "Multi-line input",
      boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
      persistentSetting: 'edit-multiline',
      callback: function(){
        Chat.inputToggleSingleMulti();
1153
1154
1155
1156
1157
1158
1159

1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
    }
    //settingsButton.click()/*for for development*/;
  })()/*#chat-settings-button setup*/;

  (function(){/*set up message preview*/
    const btnPreview = Chat.e.btnPreview;
    Chat.setPreviewText = function(t){

      this.revealPreview(true).e.previewContent.innerHTML = t;
      this.e.previewArea.querySelectorAll('a').forEach(addAnchorTargetBlank);
      this.e.inputCurrent.focus();
    };
    /**
       Reveals preview area if showIt is true, else hides it.
       This also shows/hides other elements, "as appropriate."
    */
    Chat.revealPreview = function(showIt){
      if(showIt){
        D.removeClass(Chat.e.previewArea, 'hidden');
        D.addClass([Chat.e.messagesWrapper, Chat.e.configArea],
                   'hidden');
      }else{
        D.addClass([Chat.e.configArea, Chat.e.previewArea], 'hidden');
        D.removeClass(Chat.e.messagesWrapper, 'hidden');
      }
      return this;
    };
    Chat.e.previewArea.querySelector('#chat-preview-close').
      addEventListener('click', ()=>Chat.revealPreview(false), false);
    let previewPending = false;
    const elemsToEnable = [
      btnPreview, Chat.e.btnSubmit,
      Chat.e.inputSingle, Chat.e.inputMulti];
    Chat.disableDuringAjax.push(btnPreview);
    const submit = function(ev){
      ev.preventDefault();







>
|
|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|







1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172















1173
1174
1175
1176
1177
1178
1179
1180
1181
    }
    //settingsButton.click()/*for for development*/;
  })()/*#chat-settings-button setup*/;

  (function(){/*set up message preview*/
    const btnPreview = Chat.e.btnPreview;
    Chat.setPreviewText = function(t){
      this.setCurrentView(this.e.viewPreview);
      this.e.previewContent.innerHTML = t;
      this.e.viewPreview.querySelectorAll('a').forEach(addAnchorTargetBlank);
      this.e.inputCurrent.focus();
    };















    Chat.e.viewPreview.querySelector('#chat-preview-close').
      addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false);
    let previewPending = false;
    const elemsToEnable = [
      btnPreview, Chat.e.btnSubmit,
      Chat.e.inputSingle, Chat.e.inputMulti];
    Chat.disableDuringAjax.push(btnPreview);
    const submit = function(ev){
      ev.preventDefault();
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
    const loadLegend = D.legend("Load...");
    const toolbar = Chat.e.loadOlderToolbar = D.attr(
      D.fieldset(loadLegend), "id", "load-msg-toolbar"
    );
    Chat.disableDuringAjax.push(toolbar);
    /* Loads the next n oldest messages, or all previous history if n is negative. */
    const loadOldMessages = function(n){
      Chat.e.messagesWrapper.classList.add('loading');
      Chat._isBatchLoading = true;
      const scrollHt = Chat.e.messagesWrapper.scrollHeight,
            scrollTop = Chat.e.messagesWrapper.scrollTop;
      F.fetch("chat-poll",{
        urlParams:{
          before: Chat.mnMsg,
          n: n
        },
        responseType: 'json',
        onerror:function(err){







|

|
|







1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
    const loadLegend = D.legend("Load...");
    const toolbar = Chat.e.loadOlderToolbar = D.attr(
      D.fieldset(loadLegend), "id", "load-msg-toolbar"
    );
    Chat.disableDuringAjax.push(toolbar);
    /* Loads the next n oldest messages, or all previous history if n is negative. */
    const loadOldMessages = function(n){
      Chat.e.viewMessages.classList.add('loading');
      Chat._isBatchLoading = true;
      const scrollHt = Chat.e.viewMessages.scrollHeight,
            scrollTop = Chat.e.viewMessages.scrollTop;
      F.fetch("chat-poll",{
        urlParams:{
          before: Chat.mnMsg,
          n: n
        },
        responseType: 'json',
        onerror:function(err){
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
            if(ndx>=0) Chat.disableDuringAjax.splice(ndx,1);
            Chat.e.loadOlderToolbar.disabled = true;
          }
          if(gotMessages > 0){
            F.toast.message("Loaded "+gotMessages+" older messages.");
            /* Return scroll position to where it was when the history load
               was requested, per user request */
            Chat.e.messagesWrapper.scrollTo(
              0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop
            );
          }
        },
        aftersend:function(){
          Chat.e.messagesWrapper.classList.remove('loading');
          Chat.ajaxEnd();
        }
      });
    };
    const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
    D.append(toolbar, wrapper);
    var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
    D.append(wrapper, btn);
    btn.addEventListener('click',()=>loadOldMessages(Chat.loadMessageCount));
    btn = D.button("All previous messages");
    D.append(wrapper, btn);
    btn.addEventListener('click',()=>loadOldMessages(-1));
    D.append(Chat.e.messagesWrapper, toolbar);
    toolbar.disabled = true /*will be enabled when msg load finishes */;
  })()/*end history loading widget setup*/;

  const afterFetch = function f(){
    if(true===f.isFirstCall){
      f.isFirstCall = false;
      Chat.ajaxEnd();
      Chat.e.messagesWrapper.classList.remove('loading');
      setTimeout(function(){
        Chat.scrollMessagesTo(1);
      }, 250);
    }
    if(Chat._gotServerError && Chat.intervalTimer){
      clearInterval(Chat.intervalTimer);
      Chat.reportErrorAsMessage(







|
|




|












|







|







1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
            if(ndx>=0) Chat.disableDuringAjax.splice(ndx,1);
            Chat.e.loadOlderToolbar.disabled = true;
          }
          if(gotMessages > 0){
            F.toast.message("Loaded "+gotMessages+" older messages.");
            /* Return scroll position to where it was when the history load
               was requested, per user request */
            Chat.e.viewMessages.scrollTo(
              0, Chat.e.viewMessages.scrollHeight - scrollHt + scrollTop
            );
          }
        },
        aftersend:function(){
          Chat.e.viewMessages.classList.remove('loading');
          Chat.ajaxEnd();
        }
      });
    };
    const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
    D.append(toolbar, wrapper);
    var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
    D.append(wrapper, btn);
    btn.addEventListener('click',()=>loadOldMessages(Chat.loadMessageCount));
    btn = D.button("All previous messages");
    D.append(wrapper, btn);
    btn.addEventListener('click',()=>loadOldMessages(-1));
    D.append(Chat.e.viewMessages, toolbar);
    toolbar.disabled = true /*will be enabled when msg load finishes */;
  })()/*end history loading widget setup*/;

  const afterFetch = function f(){
    if(true===f.isFirstCall){
      f.isFirstCall = false;
      Chat.ajaxEnd();
      Chat.e.viewMessages.classList.remove('loading');
      setTimeout(function(){
        Chat.scrollMessagesTo(1);
      }, 250);
    }
    if(Chat._gotServerError && Chat.intervalTimer){
      clearInterval(Chat.intervalTimer);
      Chat.reportErrorAsMessage(
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
  const poll = async function f(){
    if(f.running) return;
    f.running = true;
    Chat._isBatchLoading = f.isFirstCall;
    if(true===f.isFirstCall){
      f.isFirstCall = false;
      Chat.ajaxStart();
      Chat.e.messagesWrapper.classList.add('loading');
    }
    F.fetch("chat-poll",{
      timeout: 420 * 1000/*FIXME: get the value from the server*/,
      urlParams:{
        name: Chat.mxMsg
      },
      responseType: "json",







|







1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
  const poll = async function f(){
    if(f.running) return;
    f.running = true;
    Chat._isBatchLoading = f.isFirstCall;
    if(true===f.isFirstCall){
      f.isFirstCall = false;
      Chat.ajaxStart();
      Chat.e.viewMessages.classList.add('loading');
    }
    F.fetch("chat-poll",{
      timeout: 420 * 1000/*FIXME: get the value from the server*/,
      urlParams:{
        name: Chat.mxMsg
      },
      responseType: "json",