Fossil SCM
chat.js restructuring/cleanup - no functional changes.
Commit
12682568f0c60b58ac36a9af395bd015b0fdfd08db749bdd1c05a0f962b6ac51
Parent
b6de299bb716b3e…
1 file changed
+93
-88
+93
-88
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -155,43 +155,102 @@ | ||
| 155 | 155 | cs.e.pageTitle.innerText = cs.pageTitleOrig; |
| 156 | 156 | } |
| 157 | 157 | }, true); |
| 158 | 158 | return cs; |
| 159 | 159 | })()/*Chat initialization*/; |
| 160 | - /* State for paste and drag/drop */ | |
| 161 | - const BlobXferState = { | |
| 162 | - dropDetails: document.querySelector('#chat-drop-details'), | |
| 163 | - blob: undefined | |
| 164 | - }; | |
| 165 | - /** Updates the paste/drop zone with details of the pasted/dropped | |
| 166 | - data. The argument must be a Blob or Blob-like object (File) or | |
| 167 | - it can be falsy to reset/clear that state.*/ | |
| 168 | - const updateDropZoneContent = function(blob){ | |
| 169 | - const bx = BlobXferState, dd = bx.dropDetails; | |
| 170 | - bx.blob = blob; | |
| 171 | - D.clearElement(dd); | |
| 172 | - if(!blob){ | |
| 173 | - form.file.value = ''; | |
| 174 | - return; | |
| 175 | - } | |
| 176 | - D.append(dd, "Name: ", blob.name, | |
| 177 | - D.br(), "Size: ",blob.size); | |
| 178 | - if(blob.type && blob.type.startsWith("image/")){ | |
| 179 | - const img = D.img(); | |
| 180 | - D.append(dd, D.br(), img); | |
| 181 | - const reader = new FileReader(); | |
| 182 | - reader.onload = (e)=>img.setAttribute('src', e.target.result); | |
| 183 | - reader.readAsDataURL(blob); | |
| 184 | - } | |
| 185 | - const btn = D.button("Cancel"); | |
| 186 | - D.append(dd, D.br(), btn); | |
| 187 | - btn.addEventListener('click', ()=>updateDropZoneContent(), false); | |
| 188 | - }; | |
| 189 | - form.file.addEventListener('change', function(ev){ | |
| 190 | - //console.debug("this =",this); | |
| 191 | - updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined) | |
| 192 | - }); | |
| 160 | + | |
| 161 | + const BlobXferState = (function(){/*drag/drop bits...*/ | |
| 162 | + /* State for paste and drag/drop */ | |
| 163 | + const bxs = { | |
| 164 | + dropDetails: document.querySelector('#chat-drop-details'), | |
| 165 | + blob: undefined, | |
| 166 | + clear: function(){ | |
| 167 | + this.blob = undefined; | |
| 168 | + D.clearElement(this.dropDetails); | |
| 169 | + form.file.value = ""; | |
| 170 | + } | |
| 171 | + }; | |
| 172 | + /** Updates the paste/drop zone with details of the pasted/dropped | |
| 173 | + data. The argument must be a Blob or Blob-like object (File) or | |
| 174 | + it can be falsy to reset/clear that state.*/ | |
| 175 | + const updateDropZoneContent = function(blob){ | |
| 176 | + const dd = bxs.dropDetails; | |
| 177 | + bxs.blob = blob; | |
| 178 | + D.clearElement(dd); | |
| 179 | + if(!blob){ | |
| 180 | + form.file.value = ''; | |
| 181 | + return; | |
| 182 | + } | |
| 183 | + D.append(dd, "Name: ", blob.name, | |
| 184 | + D.br(), "Size: ",blob.size); | |
| 185 | + if(blob.type && blob.type.startsWith("image/")){ | |
| 186 | + const img = D.img(); | |
| 187 | + D.append(dd, D.br(), img); | |
| 188 | + const reader = new FileReader(); | |
| 189 | + reader.onload = (e)=>img.setAttribute('src', e.target.result); | |
| 190 | + reader.readAsDataURL(blob); | |
| 191 | + } | |
| 192 | + const btn = D.button("Cancel"); | |
| 193 | + D.append(dd, D.br(), btn); | |
| 194 | + btn.addEventListener('click', ()=>updateDropZoneContent(), false); | |
| 195 | + }; | |
| 196 | + form.file.addEventListener('change', function(ev){ | |
| 197 | + updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined) | |
| 198 | + }); | |
| 199 | + /* Handle image paste from clipboard. TODO: figure out how we can | |
| 200 | + paste non-image binary data as if it had been selected via the | |
| 201 | + file selection element. */ | |
| 202 | + document.addEventListener('paste', function(event){ | |
| 203 | + const items = event.clipboardData.items, | |
| 204 | + item = items[0]; | |
| 205 | + if(!item || !item.type) return; | |
| 206 | + else if('file'===item.kind){ | |
| 207 | + updateDropZoneContent(false/*clear prev state*/); | |
| 208 | + updateDropZoneContent(items[0].getAsFile()); | |
| 209 | + }else if(false && 'string'===item.kind){ | |
| 210 | + /* ----^^^^^ disabled for now: the intent here is that if | |
| 211 | + form.msg is not active, populate it with this text, but | |
| 212 | + whether populating it from ctrl-v when it does not have focus | |
| 213 | + is a feature or a bug is debatable. It seems useful but may | |
| 214 | + violate the Principle of Least Surprise. */ | |
| 215 | + if(document.activeElement !== form.msg){ | |
| 216 | + /* Overwrite input field if it DOES NOT have focus, | |
| 217 | + otherwise let it do its own paste handling. */ | |
| 218 | + item.getAsString((v)=>form.msg.value = v); | |
| 219 | + } | |
| 220 | + } | |
| 221 | + }, false); | |
| 222 | + /* Add help button for drag/drop/paste zone */ | |
| 223 | + form.file.parentNode.insertBefore( | |
| 224 | + F.helpButtonlets.create( | |
| 225 | + document.querySelector('#chat-input-file-area .help-buttonlet') | |
| 226 | + ), form.file | |
| 227 | + ); | |
| 228 | + //////////////////////////////////////////////////////////// | |
| 229 | + // File drag/drop visual notification. | |
| 230 | + const dropHighlight = form.file /* target zone */; | |
| 231 | + const dropEvents = { | |
| 232 | + drop: function(ev){ | |
| 233 | + D.removeClass(dropHighlight, 'dragover'); | |
| 234 | + }, | |
| 235 | + dragenter: function(ev){ | |
| 236 | + ev.preventDefault(); | |
| 237 | + ev.dataTransfer.dropEffect = "copy"; | |
| 238 | + D.addClass(dropHighlight, 'dragover'); | |
| 239 | + }, | |
| 240 | + dragleave: function(ev){ | |
| 241 | + D.removeClass(dropHighlight, 'dragover'); | |
| 242 | + }, | |
| 243 | + dragend: function(ev){ | |
| 244 | + D.removeClass(dropHighlight, 'dragover'); | |
| 245 | + } | |
| 246 | + }; | |
| 247 | + Object.keys(dropEvents).forEach( | |
| 248 | + (k)=>form.file.addEventListener(k, dropEvents[k], true) | |
| 249 | + ); | |
| 250 | + return bxs; | |
| 251 | + })()/*drag/drop*/; | |
| 193 | 252 | |
| 194 | 253 | form.addEventListener('submit',(e)=>{ |
| 195 | 254 | e.preventDefault(); |
| 196 | 255 | const fd = new FormData(form); |
| 197 | 256 | if(BlobXferState.blob/*replace file content with this*/){ |
| @@ -201,68 +260,14 @@ | ||
| 201 | 260 | fetch("chat-send",{ |
| 202 | 261 | method: 'POST', |
| 203 | 262 | body: fd |
| 204 | 263 | }); |
| 205 | 264 | } |
| 206 | - BlobXferState.blob = undefined; | |
| 207 | - D.clearElement(BlobXferState.dropDetails); | |
| 265 | + BlobXferState.clear(); | |
| 208 | 266 | form.msg.value = ""; |
| 209 | - form.file.value = ""; | |
| 210 | 267 | form.msg.focus(); |
| 211 | 268 | }); |
| 212 | - /* Handle image paste from clipboard. TODO: figure out how we can | |
| 213 | - paste non-image binary data as if it had been selected via the | |
| 214 | - file selection element. */ | |
| 215 | - document.onpaste = function(event){ | |
| 216 | - const items = event.clipboardData.items, | |
| 217 | - item = items[0]; | |
| 218 | - if(!item || !item.type) return; | |
| 219 | - else if('file'===item.kind){ | |
| 220 | - updateDropZoneContent(false/*clear prev state*/); | |
| 221 | - updateDropZoneContent(items[0].getAsFile()); | |
| 222 | - }else if(false && 'string'===item.kind){ | |
| 223 | - /* ----^^^^^ disabled for now: the intent here is that if | |
| 224 | - form.msg is not active, populate it with this text, but | |
| 225 | - whether populating it from ctrl-v when it does not have focus | |
| 226 | - is a feature or a bug is debatable. It seems useful but may | |
| 227 | - violate the Principle of Least Surprise. */ | |
| 228 | - if(document.activeElement !== form.msg){ | |
| 229 | - /* Overwrite input field if it DOES NOT have focus, | |
| 230 | - otherwise let it do its own paste handling. */ | |
| 231 | - item.getAsString((v)=>form.msg.value = v); | |
| 232 | - } | |
| 233 | - } | |
| 234 | - }; | |
| 235 | - if(true){/* Add help button for drag/drop/paste zone */ | |
| 236 | - form.file.parentNode.insertBefore( | |
| 237 | - F.helpButtonlets.create( | |
| 238 | - document.querySelector('#chat-input-file-area .help-buttonlet') | |
| 239 | - ), form.file | |
| 240 | - ); | |
| 241 | - } | |
| 242 | - //////////////////////////////////////////////////////////// | |
| 243 | - // File drag/drop visual notification. | |
| 244 | - const dropHighlight = form.file /* target zone */; | |
| 245 | - const dropEvents = { | |
| 246 | - drop: function(ev){ | |
| 247 | - D.removeClass(dropHighlight, 'dragover'); | |
| 248 | - }, | |
| 249 | - dragenter: function(ev){ | |
| 250 | - ev.preventDefault(); | |
| 251 | - ev.dataTransfer.dropEffect = "copy"; | |
| 252 | - D.addClass(dropHighlight, 'dragover'); | |
| 253 | - }, | |
| 254 | - dragleave: function(ev){ | |
| 255 | - D.removeClass(dropHighlight, 'dragover'); | |
| 256 | - }, | |
| 257 | - dragend: function(ev){ | |
| 258 | - D.removeClass(dropHighlight, 'dragover'); | |
| 259 | - } | |
| 260 | - }; | |
| 261 | - Object.keys(dropEvents).forEach( | |
| 262 | - (k)=>form.file.addEventListener(k, dropEvents[k], true) | |
| 263 | - ); | |
| 264 | 269 | |
| 265 | 270 | /* Returns a new TEXT node with the given text content. */ |
| 266 | 271 | /** Returns the local time string of Date object d, defaulting |
| 267 | 272 | to the current time. */ |
| 268 | 273 | const localTimeString = function ff(d){ |
| 269 | 274 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -155,43 +155,102 @@ | |
| 155 | cs.e.pageTitle.innerText = cs.pageTitleOrig; |
| 156 | } |
| 157 | }, true); |
| 158 | return cs; |
| 159 | })()/*Chat initialization*/; |
| 160 | /* State for paste and drag/drop */ |
| 161 | const BlobXferState = { |
| 162 | dropDetails: document.querySelector('#chat-drop-details'), |
| 163 | blob: undefined |
| 164 | }; |
| 165 | /** Updates the paste/drop zone with details of the pasted/dropped |
| 166 | data. The argument must be a Blob or Blob-like object (File) or |
| 167 | it can be falsy to reset/clear that state.*/ |
| 168 | const updateDropZoneContent = function(blob){ |
| 169 | const bx = BlobXferState, dd = bx.dropDetails; |
| 170 | bx.blob = blob; |
| 171 | D.clearElement(dd); |
| 172 | if(!blob){ |
| 173 | form.file.value = ''; |
| 174 | return; |
| 175 | } |
| 176 | D.append(dd, "Name: ", blob.name, |
| 177 | D.br(), "Size: ",blob.size); |
| 178 | if(blob.type && blob.type.startsWith("image/")){ |
| 179 | const img = D.img(); |
| 180 | D.append(dd, D.br(), img); |
| 181 | const reader = new FileReader(); |
| 182 | reader.onload = (e)=>img.setAttribute('src', e.target.result); |
| 183 | reader.readAsDataURL(blob); |
| 184 | } |
| 185 | const btn = D.button("Cancel"); |
| 186 | D.append(dd, D.br(), btn); |
| 187 | btn.addEventListener('click', ()=>updateDropZoneContent(), false); |
| 188 | }; |
| 189 | form.file.addEventListener('change', function(ev){ |
| 190 | //console.debug("this =",this); |
| 191 | updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined) |
| 192 | }); |
| 193 | |
| 194 | form.addEventListener('submit',(e)=>{ |
| 195 | e.preventDefault(); |
| 196 | const fd = new FormData(form); |
| 197 | if(BlobXferState.blob/*replace file content with this*/){ |
| @@ -201,68 +260,14 @@ | |
| 201 | fetch("chat-send",{ |
| 202 | method: 'POST', |
| 203 | body: fd |
| 204 | }); |
| 205 | } |
| 206 | BlobXferState.blob = undefined; |
| 207 | D.clearElement(BlobXferState.dropDetails); |
| 208 | form.msg.value = ""; |
| 209 | form.file.value = ""; |
| 210 | form.msg.focus(); |
| 211 | }); |
| 212 | /* Handle image paste from clipboard. TODO: figure out how we can |
| 213 | paste non-image binary data as if it had been selected via the |
| 214 | file selection element. */ |
| 215 | document.onpaste = function(event){ |
| 216 | const items = event.clipboardData.items, |
| 217 | item = items[0]; |
| 218 | if(!item || !item.type) return; |
| 219 | else if('file'===item.kind){ |
| 220 | updateDropZoneContent(false/*clear prev state*/); |
| 221 | updateDropZoneContent(items[0].getAsFile()); |
| 222 | }else if(false && 'string'===item.kind){ |
| 223 | /* ----^^^^^ disabled for now: the intent here is that if |
| 224 | form.msg is not active, populate it with this text, but |
| 225 | whether populating it from ctrl-v when it does not have focus |
| 226 | is a feature or a bug is debatable. It seems useful but may |
| 227 | violate the Principle of Least Surprise. */ |
| 228 | if(document.activeElement !== form.msg){ |
| 229 | /* Overwrite input field if it DOES NOT have focus, |
| 230 | otherwise let it do its own paste handling. */ |
| 231 | item.getAsString((v)=>form.msg.value = v); |
| 232 | } |
| 233 | } |
| 234 | }; |
| 235 | if(true){/* Add help button for drag/drop/paste zone */ |
| 236 | form.file.parentNode.insertBefore( |
| 237 | F.helpButtonlets.create( |
| 238 | document.querySelector('#chat-input-file-area .help-buttonlet') |
| 239 | ), form.file |
| 240 | ); |
| 241 | } |
| 242 | //////////////////////////////////////////////////////////// |
| 243 | // File drag/drop visual notification. |
| 244 | const dropHighlight = form.file /* target zone */; |
| 245 | const dropEvents = { |
| 246 | drop: function(ev){ |
| 247 | D.removeClass(dropHighlight, 'dragover'); |
| 248 | }, |
| 249 | dragenter: function(ev){ |
| 250 | ev.preventDefault(); |
| 251 | ev.dataTransfer.dropEffect = "copy"; |
| 252 | D.addClass(dropHighlight, 'dragover'); |
| 253 | }, |
| 254 | dragleave: function(ev){ |
| 255 | D.removeClass(dropHighlight, 'dragover'); |
| 256 | }, |
| 257 | dragend: function(ev){ |
| 258 | D.removeClass(dropHighlight, 'dragover'); |
| 259 | } |
| 260 | }; |
| 261 | Object.keys(dropEvents).forEach( |
| 262 | (k)=>form.file.addEventListener(k, dropEvents[k], true) |
| 263 | ); |
| 264 | |
| 265 | /* Returns a new TEXT node with the given text content. */ |
| 266 | /** Returns the local time string of Date object d, defaulting |
| 267 | to the current time. */ |
| 268 | const localTimeString = function ff(d){ |
| 269 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -155,43 +155,102 @@ | |
| 155 | cs.e.pageTitle.innerText = cs.pageTitleOrig; |
| 156 | } |
| 157 | }, true); |
| 158 | return cs; |
| 159 | })()/*Chat initialization*/; |
| 160 | |
| 161 | const BlobXferState = (function(){/*drag/drop bits...*/ |
| 162 | /* State for paste and drag/drop */ |
| 163 | const bxs = { |
| 164 | dropDetails: document.querySelector('#chat-drop-details'), |
| 165 | blob: undefined, |
| 166 | clear: function(){ |
| 167 | this.blob = undefined; |
| 168 | D.clearElement(this.dropDetails); |
| 169 | form.file.value = ""; |
| 170 | } |
| 171 | }; |
| 172 | /** Updates the paste/drop zone with details of the pasted/dropped |
| 173 | data. The argument must be a Blob or Blob-like object (File) or |
| 174 | it can be falsy to reset/clear that state.*/ |
| 175 | const updateDropZoneContent = function(blob){ |
| 176 | const dd = bxs.dropDetails; |
| 177 | bxs.blob = blob; |
| 178 | D.clearElement(dd); |
| 179 | if(!blob){ |
| 180 | form.file.value = ''; |
| 181 | return; |
| 182 | } |
| 183 | D.append(dd, "Name: ", blob.name, |
| 184 | D.br(), "Size: ",blob.size); |
| 185 | if(blob.type && blob.type.startsWith("image/")){ |
| 186 | const img = D.img(); |
| 187 | D.append(dd, D.br(), img); |
| 188 | const reader = new FileReader(); |
| 189 | reader.onload = (e)=>img.setAttribute('src', e.target.result); |
| 190 | reader.readAsDataURL(blob); |
| 191 | } |
| 192 | const btn = D.button("Cancel"); |
| 193 | D.append(dd, D.br(), btn); |
| 194 | btn.addEventListener('click', ()=>updateDropZoneContent(), false); |
| 195 | }; |
| 196 | form.file.addEventListener('change', function(ev){ |
| 197 | updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined) |
| 198 | }); |
| 199 | /* Handle image paste from clipboard. TODO: figure out how we can |
| 200 | paste non-image binary data as if it had been selected via the |
| 201 | file selection element. */ |
| 202 | document.addEventListener('paste', function(event){ |
| 203 | const items = event.clipboardData.items, |
| 204 | item = items[0]; |
| 205 | if(!item || !item.type) return; |
| 206 | else if('file'===item.kind){ |
| 207 | updateDropZoneContent(false/*clear prev state*/); |
| 208 | updateDropZoneContent(items[0].getAsFile()); |
| 209 | }else if(false && 'string'===item.kind){ |
| 210 | /* ----^^^^^ disabled for now: the intent here is that if |
| 211 | form.msg is not active, populate it with this text, but |
| 212 | whether populating it from ctrl-v when it does not have focus |
| 213 | is a feature or a bug is debatable. It seems useful but may |
| 214 | violate the Principle of Least Surprise. */ |
| 215 | if(document.activeElement !== form.msg){ |
| 216 | /* Overwrite input field if it DOES NOT have focus, |
| 217 | otherwise let it do its own paste handling. */ |
| 218 | item.getAsString((v)=>form.msg.value = v); |
| 219 | } |
| 220 | } |
| 221 | }, false); |
| 222 | /* Add help button for drag/drop/paste zone */ |
| 223 | form.file.parentNode.insertBefore( |
| 224 | F.helpButtonlets.create( |
| 225 | document.querySelector('#chat-input-file-area .help-buttonlet') |
| 226 | ), form.file |
| 227 | ); |
| 228 | //////////////////////////////////////////////////////////// |
| 229 | // File drag/drop visual notification. |
| 230 | const dropHighlight = form.file /* target zone */; |
| 231 | const dropEvents = { |
| 232 | drop: function(ev){ |
| 233 | D.removeClass(dropHighlight, 'dragover'); |
| 234 | }, |
| 235 | dragenter: function(ev){ |
| 236 | ev.preventDefault(); |
| 237 | ev.dataTransfer.dropEffect = "copy"; |
| 238 | D.addClass(dropHighlight, 'dragover'); |
| 239 | }, |
| 240 | dragleave: function(ev){ |
| 241 | D.removeClass(dropHighlight, 'dragover'); |
| 242 | }, |
| 243 | dragend: function(ev){ |
| 244 | D.removeClass(dropHighlight, 'dragover'); |
| 245 | } |
| 246 | }; |
| 247 | Object.keys(dropEvents).forEach( |
| 248 | (k)=>form.file.addEventListener(k, dropEvents[k], true) |
| 249 | ); |
| 250 | return bxs; |
| 251 | })()/*drag/drop*/; |
| 252 | |
| 253 | form.addEventListener('submit',(e)=>{ |
| 254 | e.preventDefault(); |
| 255 | const fd = new FormData(form); |
| 256 | if(BlobXferState.blob/*replace file content with this*/){ |
| @@ -201,68 +260,14 @@ | |
| 260 | fetch("chat-send",{ |
| 261 | method: 'POST', |
| 262 | body: fd |
| 263 | }); |
| 264 | } |
| 265 | BlobXferState.clear(); |
| 266 | form.msg.value = ""; |
| 267 | form.msg.focus(); |
| 268 | }); |
| 269 | |
| 270 | /* Returns a new TEXT node with the given text content. */ |
| 271 | /** Returns the local time string of Date object d, defaulting |
| 272 | to the current time. */ |
| 273 | const localTimeString = function ff(d){ |
| 274 |