Fossil SCM
chat: hyperlink URL-looking patterns in messages.
Commit
729e279a786dad89696110171cff843ce99b6dc0e0afbb7f2ccdaf797904cfb1
Parent
9ec2861417ef871…
1 file changed
+42
-2
+42
-2
| --- tools/chat.tcl | ||
| +++ tools/chat.tcl | ||
| @@ -73,10 +73,13 @@ | ||
| 73 | 73 | padding: 0.5em; |
| 74 | 74 | border-radius: 1em; |
| 75 | 75 | background-color: #d2dde1; |
| 76 | 76 | border: 1px solid black; |
| 77 | 77 | } |
| 78 | +\#dialog { | |
| 79 | + width: 97%; | |
| 80 | +} | |
| 78 | 81 | \#chat-input-area { |
| 79 | 82 | width: 100%; |
| 80 | 83 | display: flex; |
| 81 | 84 | flex-direction: column; |
| 82 | 85 | } |
| @@ -124,10 +127,47 @@ | ||
| 124 | 127 | } |
| 125 | 128 | form.msg.value = ""; |
| 126 | 129 | form.file.value = ""; |
| 127 | 130 | form.msg.focus(); |
| 128 | 131 | }); |
| 132 | + // Converts a message string to a message-containing DOM element | |
| 133 | + // and returns that element, which may contain child elements. | |
| 134 | + const messageToDOM = function f(str){ | |
| 135 | + "use strict"; | |
| 136 | + if(!f.rx){ | |
| 137 | + f.rx = /\\b(?:https?|ftp):\\/\\/\[a-z0-9-+&@\#\\/%?=~_|!:,.;]*\[a-z0-9-+&@\#\\/%=~_|]/gim; | |
| 138 | + // ^^^ achtung, extra backslashes needed for the outer TCL. | |
| 139 | + f.ce = (T)=>document.createElement(T); | |
| 140 | + f.ct = (T)=>document.createTextNode(T); | |
| 141 | + f.replacer = function(sub, offset){ | |
| 142 | + if(offset > f.prevStart){ | |
| 143 | + if(f.prevStart) f.accum.push(f.ct(' ')); | |
| 144 | + f.accum.push(f.ct(f.str.substring(f.prevStart, offset-1)+' ')); | |
| 145 | + } | |
| 146 | + const a = f.ce('a'); | |
| 147 | + a.setAttribute('href',sub); | |
| 148 | + a.setAttribute('target','_blank'); | |
| 149 | + a.appendChild(f.ct(sub)); | |
| 150 | + f.accum.push(a); | |
| 151 | + f.prevStart = offset + sub.length + 1; | |
| 152 | + return sub; | |
| 153 | + } | |
| 154 | + } | |
| 155 | + f.accum = []; | |
| 156 | + f.rx.lastIndex = 0; | |
| 157 | + f.prevStart = 0; | |
| 158 | + f.str = str; | |
| 159 | + str.replace(f.rx, f.replacer); | |
| 160 | + f.rx.lastIndex = 0; | |
| 161 | + delete f.str; | |
| 162 | + if(f.prevStart < str.length){ | |
| 163 | + f.accum.push(f.ct((f.prevStart ? ' ' : '')+str.substring(f.prevStart))); | |
| 164 | + } | |
| 165 | + const span = f.ce('span'); | |
| 166 | + f.accum.forEach((e)=>span.appendChild(e)); | |
| 167 | + return span; | |
| 168 | + }; | |
| 129 | 169 | function newcontent(jx){ |
| 130 | 170 | var tab = document.getElementById("dialog"); |
| 131 | 171 | var i; |
| 132 | 172 | for(i=0; i<jx.msgs.length; ++i){ |
| 133 | 173 | let m = jx.msgs[i]; |
| @@ -157,12 +197,12 @@ | ||
| 157 | 197 | } |
| 158 | 198 | let br = document.createElement("br"); |
| 159 | 199 | br.style.clear = "both"; |
| 160 | 200 | span.appendChild(br); |
| 161 | 201 | } |
| 162 | - //console.debug("m =",m); | |
| 163 | - span.appendChild(document.createTextNode(m.xmsg)); | |
| 202 | + const dmsg = messageToDOM(m.xmsg || "??empty??"); | |
| 203 | + span.appendChild(dmsg); | |
| 164 | 204 | if( m.xfrom!=_me ){ |
| 165 | 205 | span.classList.add('chat-mx'); |
| 166 | 206 | }else{ |
| 167 | 207 | span.classList.add('chat-ms'); |
| 168 | 208 | } |
| 169 | 209 |
| --- tools/chat.tcl | |
| +++ tools/chat.tcl | |
| @@ -73,10 +73,13 @@ | |
| 73 | padding: 0.5em; |
| 74 | border-radius: 1em; |
| 75 | background-color: #d2dde1; |
| 76 | border: 1px solid black; |
| 77 | } |
| 78 | \#chat-input-area { |
| 79 | width: 100%; |
| 80 | display: flex; |
| 81 | flex-direction: column; |
| 82 | } |
| @@ -124,10 +127,47 @@ | |
| 124 | } |
| 125 | form.msg.value = ""; |
| 126 | form.file.value = ""; |
| 127 | form.msg.focus(); |
| 128 | }); |
| 129 | function newcontent(jx){ |
| 130 | var tab = document.getElementById("dialog"); |
| 131 | var i; |
| 132 | for(i=0; i<jx.msgs.length; ++i){ |
| 133 | let m = jx.msgs[i]; |
| @@ -157,12 +197,12 @@ | |
| 157 | } |
| 158 | let br = document.createElement("br"); |
| 159 | br.style.clear = "both"; |
| 160 | span.appendChild(br); |
| 161 | } |
| 162 | //console.debug("m =",m); |
| 163 | span.appendChild(document.createTextNode(m.xmsg)); |
| 164 | if( m.xfrom!=_me ){ |
| 165 | span.classList.add('chat-mx'); |
| 166 | }else{ |
| 167 | span.classList.add('chat-ms'); |
| 168 | } |
| 169 |
| --- tools/chat.tcl | |
| +++ tools/chat.tcl | |
| @@ -73,10 +73,13 @@ | |
| 73 | padding: 0.5em; |
| 74 | border-radius: 1em; |
| 75 | background-color: #d2dde1; |
| 76 | border: 1px solid black; |
| 77 | } |
| 78 | \#dialog { |
| 79 | width: 97%; |
| 80 | } |
| 81 | \#chat-input-area { |
| 82 | width: 100%; |
| 83 | display: flex; |
| 84 | flex-direction: column; |
| 85 | } |
| @@ -124,10 +127,47 @@ | |
| 127 | } |
| 128 | form.msg.value = ""; |
| 129 | form.file.value = ""; |
| 130 | form.msg.focus(); |
| 131 | }); |
| 132 | // Converts a message string to a message-containing DOM element |
| 133 | // and returns that element, which may contain child elements. |
| 134 | const messageToDOM = function f(str){ |
| 135 | "use strict"; |
| 136 | if(!f.rx){ |
| 137 | f.rx = /\\b(?:https?|ftp):\\/\\/\[a-z0-9-+&@\#\\/%?=~_|!:,.;]*\[a-z0-9-+&@\#\\/%=~_|]/gim; |
| 138 | // ^^^ achtung, extra backslashes needed for the outer TCL. |
| 139 | f.ce = (T)=>document.createElement(T); |
| 140 | f.ct = (T)=>document.createTextNode(T); |
| 141 | f.replacer = function(sub, offset){ |
| 142 | if(offset > f.prevStart){ |
| 143 | if(f.prevStart) f.accum.push(f.ct(' ')); |
| 144 | f.accum.push(f.ct(f.str.substring(f.prevStart, offset-1)+' ')); |
| 145 | } |
| 146 | const a = f.ce('a'); |
| 147 | a.setAttribute('href',sub); |
| 148 | a.setAttribute('target','_blank'); |
| 149 | a.appendChild(f.ct(sub)); |
| 150 | f.accum.push(a); |
| 151 | f.prevStart = offset + sub.length + 1; |
| 152 | return sub; |
| 153 | } |
| 154 | } |
| 155 | f.accum = []; |
| 156 | f.rx.lastIndex = 0; |
| 157 | f.prevStart = 0; |
| 158 | f.str = str; |
| 159 | str.replace(f.rx, f.replacer); |
| 160 | f.rx.lastIndex = 0; |
| 161 | delete f.str; |
| 162 | if(f.prevStart < str.length){ |
| 163 | f.accum.push(f.ct((f.prevStart ? ' ' : '')+str.substring(f.prevStart))); |
| 164 | } |
| 165 | const span = f.ce('span'); |
| 166 | f.accum.forEach((e)=>span.appendChild(e)); |
| 167 | return span; |
| 168 | }; |
| 169 | function newcontent(jx){ |
| 170 | var tab = document.getElementById("dialog"); |
| 171 | var i; |
| 172 | for(i=0; i<jx.msgs.length; ++i){ |
| 173 | let m = jx.msgs[i]; |
| @@ -157,12 +197,12 @@ | |
| 197 | } |
| 198 | let br = document.createElement("br"); |
| 199 | br.style.clear = "both"; |
| 200 | span.appendChild(br); |
| 201 | } |
| 202 | const dmsg = messageToDOM(m.xmsg || "??empty??"); |
| 203 | span.appendChild(dmsg); |
| 204 | if( m.xfrom!=_me ){ |
| 205 | span.classList.add('chat-mx'); |
| 206 | }else{ |
| 207 | span.classList.add('chat-ms'); |
| 208 | } |
| 209 |