Fossil SCM

/chat input field rework, as discussed in [forum:9e85f44f864eb1f5 | forum post 9e85f44f864eb1f5]. Part 1: revert to plain text input fields, with compact-mode toggle swapping between them.

stephan 2021-10-10 04:13 trunk
Commit 136d95b6f172d9b24ed2ca0164177216cde8c13350127931c42c9d076ed1ab96
+11 -3
--- src/chat.c
+++ src/chat.c
@@ -174,15 +174,23 @@
174174
zInputPlaceholder0 =
175175
mprintf("Type markdown-formatted message for %h.", zProjectName);
176176
style_set_current_feature("chat");
177177
style_header("Chat");
178178
@ <div id='chat-input-area'>
179
- @ <div id='chat-input-line' class='single-line'>
180
- @ <div contenteditable id="chat-input-field" \
179
+ @ <div id='chat-input-line-wrapper' class='compact'>
180
+ @ <input type="text" id="chat-input-field-single" \
181
+ @ data-placeholder0="%h(zInputPlaceholder0)" \
182
+ @ data-placeholder="%h(zInputPlaceholder0)" \
183
+ @ class="chat-input-field"></input>
184
+ @ <textarea id="chat-input-field-multi" \
185
+ @ data-placeholder0="%h(zInputPlaceholder0)" \
186
+ @ data-placeholder="%h(zInputPlaceholder0)" \
187
+ @ class="chat-input-field hidden"></textarea>
188
+ @ <div contenteditable id="chat-input-field-x" \
181189
@ data-placeholder0="%h(zInputPlaceholder0)" \
182190
@ data-placeholder="%h(zInputPlaceholder0)" \
183
- @ class=""></div>
191
+ @ class="chat-input-field hidden"></div>
184192
@ <div id='chat-buttons-wrapper'>
185193
@ <span class='cbutton' id="chat-button-preview" \
186194
@ title="Preview message (Shift-Enter)">&#128065;</span>
187195
@ <span class='cbutton' id="chat-button-attach" \
188196
@ title="Attach file to message">%s(zPaperclip)</span>
189197
--- src/chat.c
+++ src/chat.c
@@ -174,15 +174,23 @@
174 zInputPlaceholder0 =
175 mprintf("Type markdown-formatted message for %h.", zProjectName);
176 style_set_current_feature("chat");
177 style_header("Chat");
178 @ <div id='chat-input-area'>
179 @ <div id='chat-input-line' class='single-line'>
180 @ <div contenteditable id="chat-input-field" \
 
 
 
 
 
 
 
 
181 @ data-placeholder0="%h(zInputPlaceholder0)" \
182 @ data-placeholder="%h(zInputPlaceholder0)" \
183 @ class=""></div>
184 @ <div id='chat-buttons-wrapper'>
185 @ <span class='cbutton' id="chat-button-preview" \
186 @ title="Preview message (Shift-Enter)">&#128065;</span>
187 @ <span class='cbutton' id="chat-button-attach" \
188 @ title="Attach file to message">%s(zPaperclip)</span>
189
--- src/chat.c
+++ src/chat.c
@@ -174,15 +174,23 @@
174 zInputPlaceholder0 =
175 mprintf("Type markdown-formatted message for %h.", zProjectName);
176 style_set_current_feature("chat");
177 style_header("Chat");
178 @ <div id='chat-input-area'>
179 @ <div id='chat-input-line-wrapper' class='compact'>
180 @ <input type="text" id="chat-input-field-single" \
181 @ data-placeholder0="%h(zInputPlaceholder0)" \
182 @ data-placeholder="%h(zInputPlaceholder0)" \
183 @ class="chat-input-field"></input>
184 @ <textarea id="chat-input-field-multi" \
185 @ data-placeholder0="%h(zInputPlaceholder0)" \
186 @ data-placeholder="%h(zInputPlaceholder0)" \
187 @ class="chat-input-field hidden"></textarea>
188 @ <div contenteditable id="chat-input-field-x" \
189 @ data-placeholder0="%h(zInputPlaceholder0)" \
190 @ data-placeholder="%h(zInputPlaceholder0)" \
191 @ class="chat-input-field hidden"></div>
192 @ <div id='chat-buttons-wrapper'>
193 @ <span class='cbutton' id="chat-button-preview" \
194 @ title="Preview message (Shift-Enter)">&#128065;</span>
195 @ <span class='cbutton' id="chat-button-attach" \
196 @ title="Attach file to message">%s(zPaperclip)</span>
197
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -94,11 +94,11 @@
9494
ht = wh;
9595
}else{
9696
elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
9797
ht = wh - extra;
9898
}
99
- f.chat.e.inputField.style.maxHeight = (ht/2)+"px";
99
+ f.chat.e.inputX.style.maxHeight = (ht/2)+"px";
100100
/* ^^^^ this is a middle ground between having no size cap
101101
on the input field and having a fixed arbitrary cap. */;
102102
contentArea.style.height =
103103
contentArea.style.maxHeight = [
104104
"calc(", (ht>=100 ? ht : 100), "px",
@@ -110,11 +110,11 @@
110110
if(false){
111111
console.debug("resized.",wh, extra, ht,
112112
window.getComputedStyle(contentArea).maxHeight,
113113
contentArea);
114114
console.debug("Set input max height to: ",
115
- f.chat.e.inputField.style.maxHeight);
115
+ f.chat.e.inputX.style.maxHeight);
116116
}
117117
};
118118
var doit;
119119
window.addEventListener('resize',function(ev){
120120
clearTimeout(doit);
@@ -131,16 +131,18 @@
131131
e:{/*map of certain DOM elements.*/
132132
messageInjectPoint: E1('#message-inject-point'),
133133
pageTitle: E1('head title'),
134134
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
135135
inputWrapper: E1("#chat-input-area"),
136
- inputLine: E1('#chat-input-line'),
136
+ inputElementWrapper: E1('#chat-input-line-wrapper'),
137137
fileSelectWrapper: E1('#chat-input-file-area'),
138138
viewMessages: E1('#chat-messages-wrapper'),
139139
btnSubmit: E1('#chat-button-submit'),
140140
btnAttach: E1('#chat-button-attach'),
141
- inputField: E1('#chat-input-field'),
141
+ inputX: E1('#chat-input-field-x'),
142
+ input1: E1('#chat-input-field-single'),
143
+ inputM: E1('#chat-input-field-multi'),
142144
inputFile: E1('#chat-input-file'),
143145
contentDiv: E1('div.content'),
144146
viewConfig: E1('#chat-config'),
145147
viewPreview: E1('#chat-preview'),
146148
previewContent: E1('#chat-preview-content'),
@@ -176,23 +178,24 @@
176178
taking into account single- vs multi-line input. The getter returns
177179
a string and the setter returns this object. */
178180
inputValue: function(){
179181
const e = this.inputElement();
180182
if(arguments.length){
181
- e.innerText = arguments[0];
183
+ if(e.isContentEditable) e.innerText = arguments[0];
184
+ else e.value = arguments[0];
182185
return this;
183186
}
184
- return e.innerText;
187
+ return e.isContentEditable ? e.innerText : e.value;
185188
},
186189
/** Asks the current user input field to take focus. Returns this. */
187190
inputFocus: function(){
188191
this.inputElement().focus();
189192
return this;
190193
},
191194
/** Returns the current message input element. */
192195
inputElement: function(){
193
- return this.e.inputField;
196
+ return this.e.inputFields[this.e.inputFields.$currentIndex];
194197
},
195198
/** Enables (if yes is truthy) or disables all elements in
196199
* this.disableDuringAjax. */
197200
enableAjaxComponents: function(yes){
198201
D[yes ? 'enable' : 'disable'](this.disableDuringAjax);
@@ -579,16 +582,22 @@
579582
D.addClassBriefly(e, a, 0, cb);
580583
}
581584
return this;
582585
}
583586
};
584
- if(D.attr(cs.e.inputField,'contenteditable','plaintext-only').isContentEditable){
587
+ cs.e.inputFields = [ cs.e.input1, cs.e.inputM, cs.e.inputX ];
588
+ cs.e.inputFields.$currentIndex = 0;
589
+ cs.e.inputFields.forEach(function(e,ndx){
590
+ if(ndx===cs.e.inputFields.$currentIndex) D.removeClass(e,'hidden');
591
+ else D.addClass(e,'hidden');
592
+ });
593
+ if(D.attr(cs.e.inputX,'contenteditable','plaintext-only').isContentEditable){
585594
cs.$browserHasPlaintextOnly = true;
586595
}else{
587596
/* Only the Chrome family supports contenteditable=plaintext-only */
588597
cs.$browserHasPlaintextOnly = false;
589
- D.attr(cs.e.inputField,'contenteditable','true');
598
+ D.attr(cs.e.inputX,'contenteditable','true');
590599
}
591600
cs.animate.$disabled = true;
592601
F.fetch.beforesend = ()=>cs.ajaxStart();
593602
F.fetch.aftersend = ()=>cs.ajaxEnd();
594603
cs.pageTitleOrig = cs.e.pageTitle.innerText;
@@ -1161,11 +1170,11 @@
11611170
/* Acrobatics to keep *some* installations of Firefox
11621171
from pasting formatting into contenteditable fields.
11631172
This also works on Chrome, but chrome has the
11641173
contenteditable=plaintext-only property which does this
11651174
for us. */
1166
- Chat.inputElement().addEventListener(
1175
+ Chat.e.inputX.addEventListener(
11671176
'paste',
11681177
function(ev){
11691178
if (ev.clipboardData && ev.clipboardData.getData) {
11701179
const pastedText = ev.clipboardData.getData('text/plain');
11711180
const selection = window.getSelection();
@@ -1185,15 +1194,12 @@
11851194
ev.dataTransfer.dropEffect = 'none';
11861195
ev.preventDefault();
11871196
ev.stopPropagation();
11881197
return false;
11891198
};
1190
-
11911199
['drop','dragenter','dragleave','dragend'].forEach(
1192
- (k)=>{
1193
- Chat.inputElement().addEventListener(k, noDragDropEvents, false);
1194
- }
1200
+ (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
11951201
);
11961202
return bxs;
11971203
})()/*drag/drop/paste*/;
11981204
11991205
const tzOffsetToString = function(off){
@@ -1323,11 +1329,13 @@
13231329
ev.stopPropagation();
13241330
Chat.submitMessage();
13251331
return false;
13261332
}
13271333
};
1328
- Chat.e.inputField.addEventListener('keydown', inputWidgetKeydown, false);
1334
+ Chat.e.inputFields.forEach(
1335
+ (e)=>e.addEventListener('keydown', inputWidgetKeydown, false)
1336
+ );
13291337
Chat.e.btnSubmit.addEventListener('click',(e)=>{
13301338
e.preventDefault();
13311339
Chat.submitMessage();
13321340
return false;
13331341
});
@@ -1571,18 +1579,34 @@
15711579
});
15721580
Chat.settings.addListener('chat-only-mode',function(s){
15731581
Chat.chatOnlyMode(s.value);
15741582
});
15751583
Chat.settings.addListener('edit-compact-mode',function(s){
1576
- Chat.e.inputLine.classList[
1584
+ if(Chat.e.inputX!==Chat.inputElement()){
1585
+ /* text field/textarea mode: swap them if needed. */
1586
+ const a = s.value
1587
+ ? [Chat.e.input1, Chat.e.inputM, 0]
1588
+ : [Chat.e.inputM, Chat.e.input1, 1];
1589
+ const v = Chat.inputValue();
1590
+ Chat.inputValue('');
1591
+ D.removeClass(a[0], 'hidden');
1592
+ D.addClass(a[1], 'hidden');
1593
+ Chat.e.inputFields.$currentIndex = a[2];
1594
+ Chat.inputValue(v);
1595
+ a[0].focus();
1596
+ }
1597
+ Chat.e.inputElementWrapper.classList[
15771598
s.value ? 'add' : 'remove'
15781599
]('compact');
15791600
});
15801601
Chat.settings.addListener('edit-ctrl-send',function(s){
15811602
const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
1582
- const eInput = Chat.inputElement();
1583
- eInput.dataset.placeholder = eInput.dataset.placeholder0 + " " +label;
1603
+ Chat.e.inputFields.forEach((e)=>{
1604
+ const v = e.dataset.placeholder0 + " " +label;
1605
+ if(e.isContentEditable) e.dataset.placeholder = v;
1606
+ else D.attr(e,'placeholder',v);
1607
+ });
15841608
Chat.e.btnSubmit.title = label;
15851609
});
15861610
const valueKludges = {
15871611
/* Convert certain string-format values to other types... */
15881612
"false": false,
@@ -1607,11 +1631,11 @@
16071631
this.inputFocus();
16081632
};
16091633
Chat.e.viewPreview.querySelector('#chat-preview-close').
16101634
addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false);
16111635
let previewPending = false;
1612
- const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputField];
1636
+ const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputFields];
16131637
const submit = function(ev){
16141638
ev.preventDefault();
16151639
ev.stopPropagation();
16161640
if(previewPending) return false;
16171641
const txt = Chat.inputValue();
16181642
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -94,11 +94,11 @@
94 ht = wh;
95 }else{
96 elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
97 ht = wh - extra;
98 }
99 f.chat.e.inputField.style.maxHeight = (ht/2)+"px";
100 /* ^^^^ this is a middle ground between having no size cap
101 on the input field and having a fixed arbitrary cap. */;
102 contentArea.style.height =
103 contentArea.style.maxHeight = [
104 "calc(", (ht>=100 ? ht : 100), "px",
@@ -110,11 +110,11 @@
110 if(false){
111 console.debug("resized.",wh, extra, ht,
112 window.getComputedStyle(contentArea).maxHeight,
113 contentArea);
114 console.debug("Set input max height to: ",
115 f.chat.e.inputField.style.maxHeight);
116 }
117 };
118 var doit;
119 window.addEventListener('resize',function(ev){
120 clearTimeout(doit);
@@ -131,16 +131,18 @@
131 e:{/*map of certain DOM elements.*/
132 messageInjectPoint: E1('#message-inject-point'),
133 pageTitle: E1('head title'),
134 loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
135 inputWrapper: E1("#chat-input-area"),
136 inputLine: E1('#chat-input-line'),
137 fileSelectWrapper: E1('#chat-input-file-area'),
138 viewMessages: E1('#chat-messages-wrapper'),
139 btnSubmit: E1('#chat-button-submit'),
140 btnAttach: E1('#chat-button-attach'),
141 inputField: E1('#chat-input-field'),
 
 
142 inputFile: E1('#chat-input-file'),
143 contentDiv: E1('div.content'),
144 viewConfig: E1('#chat-config'),
145 viewPreview: E1('#chat-preview'),
146 previewContent: E1('#chat-preview-content'),
@@ -176,23 +178,24 @@
176 taking into account single- vs multi-line input. The getter returns
177 a string and the setter returns this object. */
178 inputValue: function(){
179 const e = this.inputElement();
180 if(arguments.length){
181 e.innerText = arguments[0];
 
182 return this;
183 }
184 return e.innerText;
185 },
186 /** Asks the current user input field to take focus. Returns this. */
187 inputFocus: function(){
188 this.inputElement().focus();
189 return this;
190 },
191 /** Returns the current message input element. */
192 inputElement: function(){
193 return this.e.inputField;
194 },
195 /** Enables (if yes is truthy) or disables all elements in
196 * this.disableDuringAjax. */
197 enableAjaxComponents: function(yes){
198 D[yes ? 'enable' : 'disable'](this.disableDuringAjax);
@@ -579,16 +582,22 @@
579 D.addClassBriefly(e, a, 0, cb);
580 }
581 return this;
582 }
583 };
584 if(D.attr(cs.e.inputField,'contenteditable','plaintext-only').isContentEditable){
 
 
 
 
 
 
585 cs.$browserHasPlaintextOnly = true;
586 }else{
587 /* Only the Chrome family supports contenteditable=plaintext-only */
588 cs.$browserHasPlaintextOnly = false;
589 D.attr(cs.e.inputField,'contenteditable','true');
590 }
591 cs.animate.$disabled = true;
592 F.fetch.beforesend = ()=>cs.ajaxStart();
593 F.fetch.aftersend = ()=>cs.ajaxEnd();
594 cs.pageTitleOrig = cs.e.pageTitle.innerText;
@@ -1161,11 +1170,11 @@
1161 /* Acrobatics to keep *some* installations of Firefox
1162 from pasting formatting into contenteditable fields.
1163 This also works on Chrome, but chrome has the
1164 contenteditable=plaintext-only property which does this
1165 for us. */
1166 Chat.inputElement().addEventListener(
1167 'paste',
1168 function(ev){
1169 if (ev.clipboardData && ev.clipboardData.getData) {
1170 const pastedText = ev.clipboardData.getData('text/plain');
1171 const selection = window.getSelection();
@@ -1185,15 +1194,12 @@
1185 ev.dataTransfer.dropEffect = 'none';
1186 ev.preventDefault();
1187 ev.stopPropagation();
1188 return false;
1189 };
1190
1191 ['drop','dragenter','dragleave','dragend'].forEach(
1192 (k)=>{
1193 Chat.inputElement().addEventListener(k, noDragDropEvents, false);
1194 }
1195 );
1196 return bxs;
1197 })()/*drag/drop/paste*/;
1198
1199 const tzOffsetToString = function(off){
@@ -1323,11 +1329,13 @@
1323 ev.stopPropagation();
1324 Chat.submitMessage();
1325 return false;
1326 }
1327 };
1328 Chat.e.inputField.addEventListener('keydown', inputWidgetKeydown, false);
 
 
1329 Chat.e.btnSubmit.addEventListener('click',(e)=>{
1330 e.preventDefault();
1331 Chat.submitMessage();
1332 return false;
1333 });
@@ -1571,18 +1579,34 @@
1571 });
1572 Chat.settings.addListener('chat-only-mode',function(s){
1573 Chat.chatOnlyMode(s.value);
1574 });
1575 Chat.settings.addListener('edit-compact-mode',function(s){
1576 Chat.e.inputLine.classList[
 
 
 
 
 
 
 
 
 
 
 
 
 
1577 s.value ? 'add' : 'remove'
1578 ]('compact');
1579 });
1580 Chat.settings.addListener('edit-ctrl-send',function(s){
1581 const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
1582 const eInput = Chat.inputElement();
1583 eInput.dataset.placeholder = eInput.dataset.placeholder0 + " " +label;
 
 
 
1584 Chat.e.btnSubmit.title = label;
1585 });
1586 const valueKludges = {
1587 /* Convert certain string-format values to other types... */
1588 "false": false,
@@ -1607,11 +1631,11 @@
1607 this.inputFocus();
1608 };
1609 Chat.e.viewPreview.querySelector('#chat-preview-close').
1610 addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false);
1611 let previewPending = false;
1612 const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputField];
1613 const submit = function(ev){
1614 ev.preventDefault();
1615 ev.stopPropagation();
1616 if(previewPending) return false;
1617 const txt = Chat.inputValue();
1618
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -94,11 +94,11 @@
94 ht = wh;
95 }else{
96 elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
97 ht = wh - extra;
98 }
99 f.chat.e.inputX.style.maxHeight = (ht/2)+"px";
100 /* ^^^^ this is a middle ground between having no size cap
101 on the input field and having a fixed arbitrary cap. */;
102 contentArea.style.height =
103 contentArea.style.maxHeight = [
104 "calc(", (ht>=100 ? ht : 100), "px",
@@ -110,11 +110,11 @@
110 if(false){
111 console.debug("resized.",wh, extra, ht,
112 window.getComputedStyle(contentArea).maxHeight,
113 contentArea);
114 console.debug("Set input max height to: ",
115 f.chat.e.inputX.style.maxHeight);
116 }
117 };
118 var doit;
119 window.addEventListener('resize',function(ev){
120 clearTimeout(doit);
@@ -131,16 +131,18 @@
131 e:{/*map of certain DOM elements.*/
132 messageInjectPoint: E1('#message-inject-point'),
133 pageTitle: E1('head title'),
134 loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
135 inputWrapper: E1("#chat-input-area"),
136 inputElementWrapper: E1('#chat-input-line-wrapper'),
137 fileSelectWrapper: E1('#chat-input-file-area'),
138 viewMessages: E1('#chat-messages-wrapper'),
139 btnSubmit: E1('#chat-button-submit'),
140 btnAttach: E1('#chat-button-attach'),
141 inputX: E1('#chat-input-field-x'),
142 input1: E1('#chat-input-field-single'),
143 inputM: E1('#chat-input-field-multi'),
144 inputFile: E1('#chat-input-file'),
145 contentDiv: E1('div.content'),
146 viewConfig: E1('#chat-config'),
147 viewPreview: E1('#chat-preview'),
148 previewContent: E1('#chat-preview-content'),
@@ -176,23 +178,24 @@
178 taking into account single- vs multi-line input. The getter returns
179 a string and the setter returns this object. */
180 inputValue: function(){
181 const e = this.inputElement();
182 if(arguments.length){
183 if(e.isContentEditable) e.innerText = arguments[0];
184 else e.value = arguments[0];
185 return this;
186 }
187 return e.isContentEditable ? e.innerText : e.value;
188 },
189 /** Asks the current user input field to take focus. Returns this. */
190 inputFocus: function(){
191 this.inputElement().focus();
192 return this;
193 },
194 /** Returns the current message input element. */
195 inputElement: function(){
196 return this.e.inputFields[this.e.inputFields.$currentIndex];
197 },
198 /** Enables (if yes is truthy) or disables all elements in
199 * this.disableDuringAjax. */
200 enableAjaxComponents: function(yes){
201 D[yes ? 'enable' : 'disable'](this.disableDuringAjax);
@@ -579,16 +582,22 @@
582 D.addClassBriefly(e, a, 0, cb);
583 }
584 return this;
585 }
586 };
587 cs.e.inputFields = [ cs.e.input1, cs.e.inputM, cs.e.inputX ];
588 cs.e.inputFields.$currentIndex = 0;
589 cs.e.inputFields.forEach(function(e,ndx){
590 if(ndx===cs.e.inputFields.$currentIndex) D.removeClass(e,'hidden');
591 else D.addClass(e,'hidden');
592 });
593 if(D.attr(cs.e.inputX,'contenteditable','plaintext-only').isContentEditable){
594 cs.$browserHasPlaintextOnly = true;
595 }else{
596 /* Only the Chrome family supports contenteditable=plaintext-only */
597 cs.$browserHasPlaintextOnly = false;
598 D.attr(cs.e.inputX,'contenteditable','true');
599 }
600 cs.animate.$disabled = true;
601 F.fetch.beforesend = ()=>cs.ajaxStart();
602 F.fetch.aftersend = ()=>cs.ajaxEnd();
603 cs.pageTitleOrig = cs.e.pageTitle.innerText;
@@ -1161,11 +1170,11 @@
1170 /* Acrobatics to keep *some* installations of Firefox
1171 from pasting formatting into contenteditable fields.
1172 This also works on Chrome, but chrome has the
1173 contenteditable=plaintext-only property which does this
1174 for us. */
1175 Chat.e.inputX.addEventListener(
1176 'paste',
1177 function(ev){
1178 if (ev.clipboardData && ev.clipboardData.getData) {
1179 const pastedText = ev.clipboardData.getData('text/plain');
1180 const selection = window.getSelection();
@@ -1185,15 +1194,12 @@
1194 ev.dataTransfer.dropEffect = 'none';
1195 ev.preventDefault();
1196 ev.stopPropagation();
1197 return false;
1198 };
 
1199 ['drop','dragenter','dragleave','dragend'].forEach(
1200 (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
 
 
1201 );
1202 return bxs;
1203 })()/*drag/drop/paste*/;
1204
1205 const tzOffsetToString = function(off){
@@ -1323,11 +1329,13 @@
1329 ev.stopPropagation();
1330 Chat.submitMessage();
1331 return false;
1332 }
1333 };
1334 Chat.e.inputFields.forEach(
1335 (e)=>e.addEventListener('keydown', inputWidgetKeydown, false)
1336 );
1337 Chat.e.btnSubmit.addEventListener('click',(e)=>{
1338 e.preventDefault();
1339 Chat.submitMessage();
1340 return false;
1341 });
@@ -1571,18 +1579,34 @@
1579 });
1580 Chat.settings.addListener('chat-only-mode',function(s){
1581 Chat.chatOnlyMode(s.value);
1582 });
1583 Chat.settings.addListener('edit-compact-mode',function(s){
1584 if(Chat.e.inputX!==Chat.inputElement()){
1585 /* text field/textarea mode: swap them if needed. */
1586 const a = s.value
1587 ? [Chat.e.input1, Chat.e.inputM, 0]
1588 : [Chat.e.inputM, Chat.e.input1, 1];
1589 const v = Chat.inputValue();
1590 Chat.inputValue('');
1591 D.removeClass(a[0], 'hidden');
1592 D.addClass(a[1], 'hidden');
1593 Chat.e.inputFields.$currentIndex = a[2];
1594 Chat.inputValue(v);
1595 a[0].focus();
1596 }
1597 Chat.e.inputElementWrapper.classList[
1598 s.value ? 'add' : 'remove'
1599 ]('compact');
1600 });
1601 Chat.settings.addListener('edit-ctrl-send',function(s){
1602 const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
1603 Chat.e.inputFields.forEach((e)=>{
1604 const v = e.dataset.placeholder0 + " " +label;
1605 if(e.isContentEditable) e.dataset.placeholder = v;
1606 else D.attr(e,'placeholder',v);
1607 });
1608 Chat.e.btnSubmit.title = label;
1609 });
1610 const valueKludges = {
1611 /* Convert certain string-format values to other types... */
1612 "false": false,
@@ -1607,11 +1631,11 @@
1631 this.inputFocus();
1632 };
1633 Chat.e.viewPreview.querySelector('#chat-preview-close').
1634 addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false);
1635 let previewPending = false;
1636 const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputFields];
1637 const submit = function(ev){
1638 ev.preventDefault();
1639 ev.stopPropagation();
1640 if(previewPending) return false;
1641 const txt = Chat.inputValue();
1642
+26 -26
--- src/style.chat.css
+++ src/style.chat.css
@@ -40,13 +40,11 @@
4040
min-width: 9em /*avoid unsightly "underlap" with the neighboring
4141
.message-widget-tab element*/;
4242
white-space: normal;
4343
}
4444
body.chat.monospace-messages .message-widget-content,
45
-/*body.chat.monospace-messages textarea,*/
46
-/*body.chat.monospace-messages input[type=text],*/
47
-body.chat.monospace-messages #chat-input-field{
45
+body.chat.monospace-messages .chat-input-field{
4846
font-family: monospace;
4947
}
5048
body.chat .message-widget-content > * {
5149
margin: 0;
5250
padding: 0;
@@ -181,11 +179,11 @@
181179
/* Safari user reports that 2em is necessary to keep the file selection
182180
widget from overlapping the page footer, whereas a margin of 0 is fine
183181
for FF/Chrome (and 2em is a *huge* waste of space for those). */
184182
margin-bottom: 0;
185183
}
186
-#chat-input-field {
184
+#chat-input-field-x {
187185
display: inline-block/*supposed workaround for Chrome weirdness*/;
188186
padding: 0.2em;
189187
flex: 10 1 auto;
190188
background-color: rgba(156,156,156,0.3);
191189
overflow: auto;
@@ -195,20 +193,20 @@
195193
loses all newlines unless we explicitly set this. Chrome does not. */
196194
cursor: text;
197195
/* ^^^ In some browsers the cursor may not change for a contenteditable
198196
element until it has focus, causing potential confusion. */
199197
}
200
-#chat-input-field:empty::before {
198
+#chat-input-field-x:empty::before {
201199
content: attr(data-placeholder);
202200
opacity: 0.6;
203201
}
204
-#chat-input-field:not(:focus){
202
+.chat-input-field:not(:focus){
205203
border-width: 1px;
206204
border-style: solid;
207205
border-radius: 0.25em;
208206
}
209
-#chat-input-field:focus{
207
+.chat-input-field:focus{
210208
/* This transparent border helps avoid the text shifting around
211209
when the contenteditable attribute causes a border (which we
212210
apparently cannot style) to be added. */
213211
border-width: 1px;
214212
border-style: solid;
@@ -215,20 +213,20 @@
215213
border-color: transparent;
216214
border-radius: 0.25em;
217215
}
218216
/* Widget holding the chat message input field, send button, and
219217
settings button. */
220
-body.chat #chat-input-line {
218
+body.chat #chat-input-line-wrapper {
221219
display: flex;
222220
flex-direction: row;
223221
align-items: stretch;
224222
flex-wrap: nowrap;
225223
}
226
-/*body.chat #chat-input-line:not(.compact) {
224
+/*body.chat #chat-input-line-wrapper:not(.compact) {
227225
flex-wrap: nowrap;
228226
}*/
229
-body.chat #chat-input-line.compact {
227
+body.chat #chat-input-line-wrapper.compact {
230228
/* "The problem" with wrapping, together with a contenteditable input
231229
field, is that the latter grows as the user types, so causes
232230
wrapping to happen while they type, then to unwrap as soon as the
233231
input field is cleared (when the message is sent). When we stay
234232
wrapped in compact mode, the wrapped buttons simply take up too
@@ -241,11 +239,11 @@
241239
only way to eliminate the possibility that (A) the buttons
242240
get truncated in very narrow windows and (B) that they keep
243241
stable positions.
244242
*/
245243
}
246
-body.chat #chat-input-line.compact #chat-input-field {
244
+body.chat #chat-input-line-wrapper.compact #chat-input-field-x {
247245
}
248246
249247
body.chat #chat-buttons-wrapper {
250248
flex: 0 1 auto;
251249
display: flex;
@@ -255,11 +253,11 @@
255253
min-height: 1.5em;
256254
align-self: flex-end
257255
/*keep buttons stable at bottom/right even when input field
258256
resizes */;
259257
}
260
-body.chat #chat-input-line.compact #chat-buttons-wrapper {
258
+body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper {
261259
flex-direction: row;
262260
flex: 1 1 auto;
263261
align-self: stretch;
264262
justify-content: flex-end;
265263
/*flex-wrap: wrap;*/
@@ -285,28 +283,27 @@
285283
font-size: 130%;
286284
}
287285
body.chat #chat-buttons-wrapper > .cbutton:hover {
288286
background-color: rgba(200,200,200,0.3);
289287
}
290
-body.chat #chat-input-line.compact #chat-buttons-wrapper > .cbutton {
288
+body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper > .cbutton {
291289
margin: 2px 0.125em 0 0.125em;
292290
min-width: 6ex;
293291
max-width: 6ex;
294292
min-height: 2.3ex;
295293
max-height: 2.3ex;
296294
font-size: 120%;
297295
}
298
-body.chat #chat-input-line.compact #chat-buttons-wrapper #chat-button-submit {
296
+body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper #chat-button-submit {
299297
min-width: 12ex;
300298
}
301
-body.chat #chat-input-line:not(.compact) #chat-input-field {
302
- /*border-left-style: double;
303
- border-left-width: 3px;
304
- border-right-style: double;
305
- border-right-width: 3px;*/
299
+.chat-input-field {
300
+ font-family: inherit
301
+}
302
+body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-multi,
303
+body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-x {
306304
min-height: 4rem;
307
- /*max-height: 50rem;*/
308305
/*
309306
Problems related to max-height:
310307
311308
- If we do NOT set a max-height then pasting/typing a large amount
312309
of text can cause this element to grow without bounds, larger than
@@ -317,25 +314,25 @@
317314
318315
- If we DO set a max-height then its growth is bounded but it also
319316
cannot manually expanded by the user.
320317
321318
The lesser of the two evils seems to be to rely on the browser
322
- feature that a manual resize of the element will pin its sits.
319
+ feature that a manual resize of the element will pin its size.
323320
*/
324321
}
325322
326
-body.chat #chat-input-line > #chat-button-settings{
323
+body.chat #chat-input-line-wrapper > #chat-button-settings{
327324
margin: 0 0 0 0.25em;
328325
max-width: 2em;
329326
}
330
-body.chat #chat-input-line > input[type=text],
331
-body.chat #chat-input-line > textarea {
327
+body.chat #chat-input-line-wrapper > input[type=text],
328
+body.chat #chat-input-line-wrapper > textarea {
332329
flex: 20 1 auto;
333330
max-width: revert;
334331
min-width: 20em;
335332
}
336
-body.chat #chat-input-line.compact > input[type=text] {
333
+body.chat #chat-input-line-wrapper.compact > input[type=text] {
337334
margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/;
338335
}
339336
/* Widget holding the file selection control and preview */
340337
body.chat #chat-input-file-area {
341338
display: flex;
@@ -367,11 +364,14 @@
367364
white-space: pre;
368365
font-family: monospace;
369366
margin: auto;
370367
flex: 0;
371368
}
372
-
369
+body.chat #chat-drop-details:empty {
370
+ padding: 0;
371
+ margin: 0;
372
+}
373373
body.chat #chat-drop-details img {
374374
max-width: 45%;
375375
max-height: 45%;
376376
}
377377
body.chat .chat-view {
378378
--- src/style.chat.css
+++ src/style.chat.css
@@ -40,13 +40,11 @@
40 min-width: 9em /*avoid unsightly "underlap" with the neighboring
41 .message-widget-tab element*/;
42 white-space: normal;
43 }
44 body.chat.monospace-messages .message-widget-content,
45 /*body.chat.monospace-messages textarea,*/
46 /*body.chat.monospace-messages input[type=text],*/
47 body.chat.monospace-messages #chat-input-field{
48 font-family: monospace;
49 }
50 body.chat .message-widget-content > * {
51 margin: 0;
52 padding: 0;
@@ -181,11 +179,11 @@
181 /* Safari user reports that 2em is necessary to keep the file selection
182 widget from overlapping the page footer, whereas a margin of 0 is fine
183 for FF/Chrome (and 2em is a *huge* waste of space for those). */
184 margin-bottom: 0;
185 }
186 #chat-input-field {
187 display: inline-block/*supposed workaround for Chrome weirdness*/;
188 padding: 0.2em;
189 flex: 10 1 auto;
190 background-color: rgba(156,156,156,0.3);
191 overflow: auto;
@@ -195,20 +193,20 @@
195 loses all newlines unless we explicitly set this. Chrome does not. */
196 cursor: text;
197 /* ^^^ In some browsers the cursor may not change for a contenteditable
198 element until it has focus, causing potential confusion. */
199 }
200 #chat-input-field:empty::before {
201 content: attr(data-placeholder);
202 opacity: 0.6;
203 }
204 #chat-input-field:not(:focus){
205 border-width: 1px;
206 border-style: solid;
207 border-radius: 0.25em;
208 }
209 #chat-input-field:focus{
210 /* This transparent border helps avoid the text shifting around
211 when the contenteditable attribute causes a border (which we
212 apparently cannot style) to be added. */
213 border-width: 1px;
214 border-style: solid;
@@ -215,20 +213,20 @@
215 border-color: transparent;
216 border-radius: 0.25em;
217 }
218 /* Widget holding the chat message input field, send button, and
219 settings button. */
220 body.chat #chat-input-line {
221 display: flex;
222 flex-direction: row;
223 align-items: stretch;
224 flex-wrap: nowrap;
225 }
226 /*body.chat #chat-input-line:not(.compact) {
227 flex-wrap: nowrap;
228 }*/
229 body.chat #chat-input-line.compact {
230 /* "The problem" with wrapping, together with a contenteditable input
231 field, is that the latter grows as the user types, so causes
232 wrapping to happen while they type, then to unwrap as soon as the
233 input field is cleared (when the message is sent). When we stay
234 wrapped in compact mode, the wrapped buttons simply take up too
@@ -241,11 +239,11 @@
241 only way to eliminate the possibility that (A) the buttons
242 get truncated in very narrow windows and (B) that they keep
243 stable positions.
244 */
245 }
246 body.chat #chat-input-line.compact #chat-input-field {
247 }
248
249 body.chat #chat-buttons-wrapper {
250 flex: 0 1 auto;
251 display: flex;
@@ -255,11 +253,11 @@
255 min-height: 1.5em;
256 align-self: flex-end
257 /*keep buttons stable at bottom/right even when input field
258 resizes */;
259 }
260 body.chat #chat-input-line.compact #chat-buttons-wrapper {
261 flex-direction: row;
262 flex: 1 1 auto;
263 align-self: stretch;
264 justify-content: flex-end;
265 /*flex-wrap: wrap;*/
@@ -285,28 +283,27 @@
285 font-size: 130%;
286 }
287 body.chat #chat-buttons-wrapper > .cbutton:hover {
288 background-color: rgba(200,200,200,0.3);
289 }
290 body.chat #chat-input-line.compact #chat-buttons-wrapper > .cbutton {
291 margin: 2px 0.125em 0 0.125em;
292 min-width: 6ex;
293 max-width: 6ex;
294 min-height: 2.3ex;
295 max-height: 2.3ex;
296 font-size: 120%;
297 }
298 body.chat #chat-input-line.compact #chat-buttons-wrapper #chat-button-submit {
299 min-width: 12ex;
300 }
301 body.chat #chat-input-line:not(.compact) #chat-input-field {
302 /*border-left-style: double;
303 border-left-width: 3px;
304 border-right-style: double;
305 border-right-width: 3px;*/
306 min-height: 4rem;
307 /*max-height: 50rem;*/
308 /*
309 Problems related to max-height:
310
311 - If we do NOT set a max-height then pasting/typing a large amount
312 of text can cause this element to grow without bounds, larger than
@@ -317,25 +314,25 @@
317
318 - If we DO set a max-height then its growth is bounded but it also
319 cannot manually expanded by the user.
320
321 The lesser of the two evils seems to be to rely on the browser
322 feature that a manual resize of the element will pin its sits.
323 */
324 }
325
326 body.chat #chat-input-line > #chat-button-settings{
327 margin: 0 0 0 0.25em;
328 max-width: 2em;
329 }
330 body.chat #chat-input-line > input[type=text],
331 body.chat #chat-input-line > textarea {
332 flex: 20 1 auto;
333 max-width: revert;
334 min-width: 20em;
335 }
336 body.chat #chat-input-line.compact > input[type=text] {
337 margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/;
338 }
339 /* Widget holding the file selection control and preview */
340 body.chat #chat-input-file-area {
341 display: flex;
@@ -367,11 +364,14 @@
367 white-space: pre;
368 font-family: monospace;
369 margin: auto;
370 flex: 0;
371 }
372
 
 
 
373 body.chat #chat-drop-details img {
374 max-width: 45%;
375 max-height: 45%;
376 }
377 body.chat .chat-view {
378
--- src/style.chat.css
+++ src/style.chat.css
@@ -40,13 +40,11 @@
40 min-width: 9em /*avoid unsightly "underlap" with the neighboring
41 .message-widget-tab element*/;
42 white-space: normal;
43 }
44 body.chat.monospace-messages .message-widget-content,
45 body.chat.monospace-messages .chat-input-field{
 
 
46 font-family: monospace;
47 }
48 body.chat .message-widget-content > * {
49 margin: 0;
50 padding: 0;
@@ -181,11 +179,11 @@
179 /* Safari user reports that 2em is necessary to keep the file selection
180 widget from overlapping the page footer, whereas a margin of 0 is fine
181 for FF/Chrome (and 2em is a *huge* waste of space for those). */
182 margin-bottom: 0;
183 }
184 #chat-input-field-x {
185 display: inline-block/*supposed workaround for Chrome weirdness*/;
186 padding: 0.2em;
187 flex: 10 1 auto;
188 background-color: rgba(156,156,156,0.3);
189 overflow: auto;
@@ -195,20 +193,20 @@
193 loses all newlines unless we explicitly set this. Chrome does not. */
194 cursor: text;
195 /* ^^^ In some browsers the cursor may not change for a contenteditable
196 element until it has focus, causing potential confusion. */
197 }
198 #chat-input-field-x:empty::before {
199 content: attr(data-placeholder);
200 opacity: 0.6;
201 }
202 .chat-input-field:not(:focus){
203 border-width: 1px;
204 border-style: solid;
205 border-radius: 0.25em;
206 }
207 .chat-input-field:focus{
208 /* This transparent border helps avoid the text shifting around
209 when the contenteditable attribute causes a border (which we
210 apparently cannot style) to be added. */
211 border-width: 1px;
212 border-style: solid;
@@ -215,20 +213,20 @@
213 border-color: transparent;
214 border-radius: 0.25em;
215 }
216 /* Widget holding the chat message input field, send button, and
217 settings button. */
218 body.chat #chat-input-line-wrapper {
219 display: flex;
220 flex-direction: row;
221 align-items: stretch;
222 flex-wrap: nowrap;
223 }
224 /*body.chat #chat-input-line-wrapper:not(.compact) {
225 flex-wrap: nowrap;
226 }*/
227 body.chat #chat-input-line-wrapper.compact {
228 /* "The problem" with wrapping, together with a contenteditable input
229 field, is that the latter grows as the user types, so causes
230 wrapping to happen while they type, then to unwrap as soon as the
231 input field is cleared (when the message is sent). When we stay
232 wrapped in compact mode, the wrapped buttons simply take up too
@@ -241,11 +239,11 @@
239 only way to eliminate the possibility that (A) the buttons
240 get truncated in very narrow windows and (B) that they keep
241 stable positions.
242 */
243 }
244 body.chat #chat-input-line-wrapper.compact #chat-input-field-x {
245 }
246
247 body.chat #chat-buttons-wrapper {
248 flex: 0 1 auto;
249 display: flex;
@@ -255,11 +253,11 @@
253 min-height: 1.5em;
254 align-self: flex-end
255 /*keep buttons stable at bottom/right even when input field
256 resizes */;
257 }
258 body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper {
259 flex-direction: row;
260 flex: 1 1 auto;
261 align-self: stretch;
262 justify-content: flex-end;
263 /*flex-wrap: wrap;*/
@@ -285,28 +283,27 @@
283 font-size: 130%;
284 }
285 body.chat #chat-buttons-wrapper > .cbutton:hover {
286 background-color: rgba(200,200,200,0.3);
287 }
288 body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper > .cbutton {
289 margin: 2px 0.125em 0 0.125em;
290 min-width: 6ex;
291 max-width: 6ex;
292 min-height: 2.3ex;
293 max-height: 2.3ex;
294 font-size: 120%;
295 }
296 body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper #chat-button-submit {
297 min-width: 12ex;
298 }
299 .chat-input-field {
300 font-family: inherit
301 }
302 body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-multi,
303 body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-x {
304 min-height: 4rem;
 
305 /*
306 Problems related to max-height:
307
308 - If we do NOT set a max-height then pasting/typing a large amount
309 of text can cause this element to grow without bounds, larger than
@@ -317,25 +314,25 @@
314
315 - If we DO set a max-height then its growth is bounded but it also
316 cannot manually expanded by the user.
317
318 The lesser of the two evils seems to be to rely on the browser
319 feature that a manual resize of the element will pin its size.
320 */
321 }
322
323 body.chat #chat-input-line-wrapper > #chat-button-settings{
324 margin: 0 0 0 0.25em;
325 max-width: 2em;
326 }
327 body.chat #chat-input-line-wrapper > input[type=text],
328 body.chat #chat-input-line-wrapper > textarea {
329 flex: 20 1 auto;
330 max-width: revert;
331 min-width: 20em;
332 }
333 body.chat #chat-input-line-wrapper.compact > input[type=text] {
334 margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/;
335 }
336 /* Widget holding the file selection control and preview */
337 body.chat #chat-input-file-area {
338 display: flex;
@@ -367,11 +364,14 @@
364 white-space: pre;
365 font-family: monospace;
366 margin: auto;
367 flex: 0;
368 }
369 body.chat #chat-drop-details:empty {
370 padding: 0;
371 margin: 0;
372 }
373 body.chat #chat-drop-details img {
374 max-width: 45%;
375 max-height: 45%;
376 }
377 body.chat .chat-view {
378

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button